Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 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 | import android.support.checkapi.ApiXmlConversionTask |
| 18 | import android.support.checkapi.CheckApiTask |
| 19 | import android.support.checkapi.UpdateApiTask |
| 20 | import android.support.doclava.DoclavaMultilineJavadocOptionFileOption |
| 21 | import android.support.doclava.DoclavaTask |
| 22 | import android.support.jdiff.JDiffTask |
Alan Viverette | 86aad03 | 2017-02-07 15:05:16 -0500 | [diff] [blame] | 23 | |
| 24 | import org.gradle.api.InvalidUserDataException |
| 25 | |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 26 | import groovy.io.FileType |
| 27 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 28 | import java.util.regex.Matcher |
| 29 | import java.util.regex.Pattern |
| 30 | |
Alan Viverette | 810a148 | 2017-03-20 12:43:43 -0400 | [diff] [blame] | 31 | // Set up platform API files for federation. |
| 32 | if (project.androidApiTxt != null) { |
| 33 | task generateSdkApi(type: Copy) { |
| 34 | description = 'Copies the API files for the current SDK.' |
Alan Viverette | eb9f332 | 2017-03-09 16:29:57 -0500 | [diff] [blame] | 35 | |
Alan Viverette | 810a148 | 2017-03-20 12:43:43 -0400 | [diff] [blame] | 36 | // Export the API files so this looks like a DoclavaTask. |
| 37 | ext.apiFile = new File(project.docsDir, 'release/sdk_current.txt') |
| 38 | ext.removedApiFile = new File(project.docsDir, 'release/sdk_removed.txt') |
Alan Viverette | eb9f332 | 2017-03-09 16:29:57 -0500 | [diff] [blame] | 39 | |
Alan Viverette | 810a148 | 2017-03-20 12:43:43 -0400 | [diff] [blame] | 40 | from project.androidApiTxt.absolutePath |
| 41 | into apiFile.parent |
| 42 | rename { apiFile.name } |
Alan Viverette | eb9f332 | 2017-03-09 16:29:57 -0500 | [diff] [blame] | 43 | |
Alan Viverette | 810a148 | 2017-03-20 12:43:43 -0400 | [diff] [blame] | 44 | // Register the fake removed file as an output. |
| 45 | outputs.file removedApiFile |
| 46 | |
| 47 | doLast { |
| 48 | removedApiFile.createNewFile() |
| 49 | } |
| 50 | } |
| 51 | } else { |
| 52 | task generateSdkApi(type: DoclavaTask, dependsOn: [configurations.doclava]) { |
| 53 | description = 'Generates API files for the current SDK.' |
| 54 | |
| 55 | docletpath = configurations.doclava.resolve() |
| 56 | destinationDir = project.docsDir |
| 57 | |
| 58 | classpath = project.androidJar |
| 59 | source zipTree(project.androidSrcJar) |
| 60 | |
| 61 | apiFile = new File(project.docsDir, 'release/sdk_current.txt') |
| 62 | removedApiFile = new File(project.docsDir, 'release/sdk_removed.txt') |
| 63 | generateDocs = false |
| 64 | |
| 65 | options { |
| 66 | addStringOption "stubpackages", "android.*" |
| 67 | } |
Alan Viverette | eb9f332 | 2017-03-09 16:29:57 -0500 | [diff] [blame] | 68 | } |
| 69 | } |
| 70 | |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 71 | // Generates online docs. |
Alan Viverette | eb9f332 | 2017-03-09 16:29:57 -0500 | [diff] [blame] | 72 | task generateDocs(type: DoclavaTask, dependsOn: [configurations.doclava, generateSdkApi]) { |
Alan Viverette | 5b3946f | 2017-04-27 09:59:22 -0400 | [diff] [blame] | 73 | def offlineDocs = project.docs.offline |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 74 | group = JavaBasePlugin.DOCUMENTATION_GROUP |
Siyamed Sinir | c39846b | 2017-04-13 20:58:56 -0700 | [diff] [blame] | 75 | description = 'Generates d.android.com-style documentation. To generate offline docs use ' + |
| 76 | '\'-PofflineDocs=true\' parameter.' |
Alan Viverette | 3be55eb | 2017-02-23 14:39:17 -0500 | [diff] [blame] | 77 | |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 78 | docletpath = configurations.doclava.resolve() |
Yigit Boyar | 63e6c67 | 2017-03-31 13:32:17 -0700 | [diff] [blame] | 79 | destinationDir = new File(project.docsDir, offlineDocs ? "offline" : "online") |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 80 | |
| 81 | // Base classpath is Android SDK, sub-projects add their own. |
| 82 | classpath = project.ext.androidJar |
| 83 | |
| 84 | def hdfOption = new DoclavaMultilineJavadocOptionFileOption('hdf') |
| 85 | hdfOption.add( |
| 86 | ['android.whichdoc', 'online'], |
Scott Main | 9073d9b | 2017-03-27 11:47:20 -0700 | [diff] [blame] | 87 | ['android.hasSamples', 'true'], |
| 88 | ['dac', 'true']) |
Alan Viverette | d953ba0 | 2017-02-23 14:39:17 -0500 | [diff] [blame] | 89 | |
Alan Viverette | eb9f332 | 2017-03-09 16:29:57 -0500 | [diff] [blame] | 90 | def federateOption = new DoclavaMultilineJavadocOptionFileOption('federate') |
| 91 | federateOption.add(['Android', 'https://developer.android.com']) |
| 92 | |
| 93 | def federationapiOption = new DoclavaMultilineJavadocOptionFileOption('federationapi') |
| 94 | federationapiOption.add(['Android', generateSdkApi.apiFile.absolutePath]) |
| 95 | |
Alan Viverette | d953ba0 | 2017-02-23 14:39:17 -0500 | [diff] [blame] | 96 | // Track API change history. |
| 97 | def apiFilePattern = /(\d+\.\d+\.\d).txt/ |
| 98 | def sinceOption = new DoclavaMultilineJavadocOptionFileOption('since') |
| 99 | File apiDir = new File(supportRootFolder, 'api') |
| 100 | apiDir.eachFileMatch FileType.FILES, ~apiFilePattern, { File apiFile -> |
| 101 | def apiLevel = (apiFile.name =~ apiFilePattern)[0][1] |
| 102 | sinceOption.add([apiFile.absolutePath, apiLevel]) |
| 103 | } |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 104 | |
| 105 | // Default hidden errors + hidden superclass (111) and |
| 106 | // deprecation mismatch (113) to match framework docs. |
Aurimas Liutikas | 058995d | 2017-04-11 12:40:01 -0700 | [diff] [blame] | 107 | final def hidden = [105, 106, 107, 111, 112, 113, 115, 116, 121] |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 108 | |
| 109 | doclavaErrors = (101..122) - hidden |
| 110 | doclavaWarnings = [] |
| 111 | doclavaHidden += hidden |
| 112 | |
| 113 | options { |
| 114 | addStringOption "templatedir", |
Scott Main | e8eb63e | 2017-03-08 11:12:02 -0800 | [diff] [blame] | 115 | "${supportRootFolder}/../../external/doclava/res/assets/templates-sdk" |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 116 | addStringOption "stubpackages", "android.support.*" |
| 117 | addStringOption "samplesdir", "${supportRootFolder}/samples" |
Alan Viverette | eb9f332 | 2017-03-09 16:29:57 -0500 | [diff] [blame] | 118 | addOption federateOption |
| 119 | addOption federationapiOption |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 120 | addOption hdfOption |
Alan Viverette | d953ba0 | 2017-02-23 14:39:17 -0500 | [diff] [blame] | 121 | addOption sinceOption |
Alan Viverette | cef9526 | 2017-03-24 16:34:37 -0400 | [diff] [blame] | 122 | |
| 123 | // Specific to reference docs. |
Yigit Boyar | 63e6c67 | 2017-03-31 13:32:17 -0700 | [diff] [blame] | 124 | if (!offlineDocs) { |
| 125 | addStringOption "toroot", "/" |
| 126 | addBooleanOption "devsite", true |
Alan Viverette | 5b3946f | 2017-04-27 09:59:22 -0400 | [diff] [blame] | 127 | addStringOption "dac_libraryroot", project.docs.dac.libraryroot |
| 128 | addStringOption "dac_dataname", project.docs.dac.dataname |
Yigit Boyar | 63e6c67 | 2017-03-31 13:32:17 -0700 | [diff] [blame] | 129 | } |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 130 | } |
| 131 | |
| 132 | exclude '**/BuildConfig.java' |
| 133 | } |
| 134 | |
Alan Viverette | 3be55eb | 2017-02-23 14:39:17 -0500 | [diff] [blame] | 135 | // Generates a distribution artifact for online docs. |
| 136 | task distDocs(type: Zip, dependsOn: generateDocs) { |
| 137 | group = JavaBasePlugin.DOCUMENTATION_GROUP |
| 138 | description = 'Generates distribution artifact for d.android.com-style documentation.' |
| 139 | |
| 140 | from generateDocs.destinationDir |
| 141 | destinationDir project.distDir |
| 142 | baseName = "android-support-docs" |
| 143 | version = project.buildNumber |
| 144 | |
| 145 | doLast { |
Alan Viverette | cef9526 | 2017-03-24 16:34:37 -0400 | [diff] [blame] | 146 | logger.lifecycle("'Wrote API reference to ${archivePath}") |
Alan Viverette | 3be55eb | 2017-02-23 14:39:17 -0500 | [diff] [blame] | 147 | } |
| 148 | } |
| 149 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 150 | def MSG_HIDE_API = |
| 151 | "If you are adding APIs that should be excluded from the public API surface,\n" + |
| 152 | "consider using package or private visibility. If the API must have public\n" + |
| 153 | "visibility, you may exclude it from public API by using the @hide javadoc\n" + |
| 154 | "annotation paired with the @RestrictTo(LIBRARY_GROUP) code annotation." |
| 155 | |
| 156 | // Check that the API we're building hasn't broken compatibility with the |
| 157 | // previously released version. These types of changes are forbidden. |
| 158 | def CHECK_API_CONFIG_RELEASE = [ |
| 159 | onFailMessage: |
| 160 | "Compatibility with previously released public APIs has been broken. Please\n" + |
| 161 | "verify your change with Support API Council and provide error output,\n" + |
| 162 | "including the error messages and associated SHAs.\n" + |
| 163 | "\n" + |
| 164 | "If you are removing APIs, they must be deprecated first before being removed\n" + |
| 165 | "in a subsequent release.\n" + |
| 166 | "\n" + MSG_HIDE_API, |
| 167 | errors: (7..18), |
| 168 | warnings: [], |
| 169 | hidden: (2..6) + (19..30) |
| 170 | ] |
| 171 | |
| 172 | // Check that the API we're building hasn't changed from the development |
| 173 | // version. These types of changes require an explicit API file update. |
| 174 | def CHECK_API_CONFIG_DEVELOP = [ |
| 175 | onFailMessage: |
| 176 | "Public API definition has changed. Please run ./gradlew updateApi to confirm\n" + |
| 177 | "these changes are intentional by updating the public API definition.\n" + |
| 178 | "\n" + MSG_HIDE_API, |
| 179 | errors: (2..30)-[22], |
| 180 | warnings: [], |
| 181 | hidden: [22] |
| 182 | ] |
| 183 | |
| 184 | // This is a patch or finalized release. Check that the API we're building |
| 185 | // hasn't changed from the current. |
| 186 | def CHECK_API_CONFIG_PATCH = [ |
| 187 | onFailMessage: |
| 188 | "Public API definition may not change in finalized or patch releases.\n" + |
| 189 | "\n" + MSG_HIDE_API, |
| 190 | errors: (2..30)-[22], |
| 191 | warnings: [], |
| 192 | hidden: [22] |
| 193 | ] |
| 194 | |
| 195 | CheckApiTask createCheckApiTask(String taskName, def checkApiConfig, File oldApi, File newApi, |
| 196 | File whitelist = null) { |
| 197 | return tasks.create(name: taskName, type: CheckApiTask.class) { |
| 198 | doclavaClasspath = generateApi.docletpath |
| 199 | |
| 200 | onFailMessage = checkApiConfig.onFailMessage |
| 201 | checkApiErrors = checkApiConfig.errors |
| 202 | checkApiWarnings = checkApiConfig.warnings |
| 203 | checkApiHidden = checkApiConfig.hidden |
| 204 | |
| 205 | newApiFile = newApi |
| 206 | oldApiFile = oldApi |
| 207 | newRemovedApiFile = new File(project.docsDir, 'release/removed.txt') |
| 208 | oldRemovedApiFile = new File(supportRootFolder, 'api/removed.txt') |
| 209 | |
| 210 | whitelistErrorsFile = whitelist |
| 211 | |
Alan Viverette | f354ae5 | 2017-04-26 14:22:38 -0400 | [diff] [blame] | 212 | doFirst { |
| 213 | logger.lifecycle "Verifying ${newApi.name} against ${oldApi.name}..." |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 214 | } |
| 215 | } |
| 216 | } |
| 217 | |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 218 | // Generates API files. |
| 219 | task generateApi(type: DoclavaTask, dependsOn: configurations.doclava) { |
| 220 | docletpath = configurations.doclava.resolve() |
| 221 | destinationDir = project.docsDir |
| 222 | |
| 223 | // Base classpath is Android SDK, sub-projects add their own. |
| 224 | classpath = project.ext.androidJar |
| 225 | |
| 226 | apiFile = new File(project.docsDir, 'release/current.txt') |
| 227 | removedApiFile = new File(project.docsDir, 'release/removed.txt') |
| 228 | generateDocs = false |
| 229 | |
| 230 | options { |
| 231 | addStringOption "templatedir", |
Scott Main | e8eb63e | 2017-03-08 11:12:02 -0800 | [diff] [blame] | 232 | "${supportRootFolder}/../../external/doclava/res/assets/templates-sdk" |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 233 | addStringOption "stubpackages", "android.support.*" |
| 234 | } |
| 235 | exclude '**/BuildConfig.java' |
| 236 | exclude '**/R.java' |
| 237 | } |
| 238 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 239 | /** |
| 240 | * Returns the most recent API, optionally restricting to APIs before |
| 241 | * <code>beforeApi</code>. |
| 242 | * |
| 243 | * @param refApi the reference API version, ex. 25.0.0-SNAPSHOT |
| 244 | * @return the most recently released API file |
| 245 | */ |
| 246 | File getApiFile(String refApi = supportVersion, boolean previous = false, boolean release = false) { |
| 247 | def refMatcher = refApi =~ /^(\d+)\.(\d+)\.(\d+)(-.+)?$/ |
| 248 | def refMajor = refMatcher[0][1] as int |
| 249 | def refMinor = refMatcher[0][2] as int |
| 250 | def refPatch = refMatcher[0][3] as int |
| 251 | def refExtra = refMatcher[0][4] |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 252 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 253 | File apiDir = new File(ext.supportRootFolder, 'api') |
Alan Viverette | 86aad03 | 2017-02-07 15:05:16 -0500 | [diff] [blame] | 254 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 255 | if (!previous) { |
| 256 | // If this is a patch or release version, ignore the extra. |
| 257 | return new File(apiDir, "$refMajor.$refMinor.0" + |
| 258 | (refPatch || release ? "" : refExtra) + ".txt") |
Alan Viverette | 86aad03 | 2017-02-07 15:05:16 -0500 | [diff] [blame] | 259 | } |
| 260 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 261 | File lastFile = null |
| 262 | def lastMajor |
| 263 | def lastMinor |
Alan Viverette | 86aad03 | 2017-02-07 15:05:16 -0500 | [diff] [blame] | 264 | |
Alan Viverette | f354ae5 | 2017-04-26 14:22:38 -0400 | [diff] [blame] | 265 | // Only look at released versions and snapshots thereof, ex. X.Y.0.txt or X.Y.0-SNAPSHOT.txt. |
| 266 | apiDir.eachFileMatch FileType.FILES, ~/(\d+)\.(\d+)\.0(-SNAPSHOT)?\.txt/, { File file -> |
| 267 | def matcher = file.name =~ /(\d+)\.(\d+)\.0(-SNAPSHOT)?\.txt/ |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 268 | def major = matcher[0][1] as int |
| 269 | def minor = matcher[0][2] as int |
Alan Viverette | 86aad03 | 2017-02-07 15:05:16 -0500 | [diff] [blame] | 270 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 271 | if (lastFile == null || major > lastMajor || (major == lastMajor && minor > lastMinor)) { |
| 272 | if (refMajor > major || (refMajor == major && refMinor > minor)) { |
| 273 | lastFile = file |
| 274 | lastMajor = major; |
| 275 | lastMinor = minor; |
| 276 | } |
Alan Viverette | 86aad03 | 2017-02-07 15:05:16 -0500 | [diff] [blame] | 277 | } |
| 278 | } |
| 279 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 280 | return lastFile |
Alan Viverette | 86aad03 | 2017-02-07 15:05:16 -0500 | [diff] [blame] | 281 | } |
| 282 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 283 | String stripExtension(String fileName) { |
| 284 | return fileName[0..fileName.lastIndexOf('.')-1] |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 285 | } |
| 286 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 287 | // Make sure the API surface has not broken since the last release. |
| 288 | def isPatchVersion = supportVersion ==~ /\d+\.\d+.[1-9]\d*(-.+)?/ |
| 289 | def isSnapshotVersion = supportVersion ==~ /\d+\.\d+.\d+-SNAPSHOT/ |
| 290 | def previousApiFile = getApiFile(project.supportVersion, !isPatchVersion) |
| 291 | def whitelistFile = new File( |
| 292 | previousApiFile.parentFile, stripExtension(previousApiFile.name) + ".ignore") |
| 293 | def checkApiRelease = createCheckApiTask("checkApiRelease", CHECK_API_CONFIG_RELEASE, |
| 294 | previousApiFile, generateApi.apiFile, whitelistFile).dependsOn(generateApi) |
| 295 | |
| 296 | // Allow a comma-delimited list of whitelisted errors. |
| 297 | if (project.hasProperty("ignore")) { |
| 298 | checkApiRelease.whitelistErrors = ignore.split(',') |
| 299 | } |
| 300 | |
| 301 | // Check whether the development API surface has changed. |
| 302 | def verifyConfig = isPatchVersion != 0 ? CHECK_API_CONFIG_DEVELOP : CHECK_API_CONFIG_PATCH; |
| 303 | def checkApi = createCheckApiTask("checkApi", verifyConfig, getApiFile(), generateApi.apiFile) |
| 304 | .dependsOn(generateApi, checkApiRelease) |
| 305 | |
| 306 | checkApi.group JavaBasePlugin.VERIFICATION_GROUP |
| 307 | checkApi.description 'Verify the API surface.' |
| 308 | |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 309 | rootProject.createArchive.dependsOn checkApi |
| 310 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 311 | task verifyUpdateApiAllowed() { |
| 312 | // This could be moved to doFirst inside updateApi, but using it as a |
| 313 | // dependency with no inputs forces it to run even when updateApi is a |
| 314 | // no-op. |
| 315 | doLast { |
| 316 | if (isPatchVersion) { |
| 317 | throw new GradleException("Public APIs may not be modified in patch releases.") |
| 318 | } else if (isSnapshotVersion && getApiFile(supportVersion, false, true).exists()) { |
| 319 | throw new GradleException("Inconsistent version. Public API file already exists.") |
| 320 | } else if (!isSnapshotVersion && getApiFile().exists() && !project.hasProperty("force")) { |
| 321 | throw new GradleException("Public APIs may not be modified in finalized releases.") |
| 322 | } |
| 323 | } |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 324 | } |
| 325 | |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 326 | task updateApi(type: UpdateApiTask, dependsOn: [checkApiRelease, verifyUpdateApiAllowed]) { |
| 327 | group JavaBasePlugin.VERIFICATION_GROUP |
| 328 | description 'Updates the candidate API file to incorporate valid changes.' |
| 329 | newApiFile = checkApiRelease.newApiFile |
| 330 | oldApiFile = getApiFile() |
| 331 | newRemovedApiFile = new File(project.docsDir, 'release/removed.txt') |
| 332 | oldRemovedApiFile = new File(supportRootFolder, 'api/removed.txt') |
| 333 | whitelistErrors = checkApiRelease.whitelistErrors |
| 334 | whitelistErrorsFile = checkApiRelease.whitelistErrorsFile |
| 335 | } |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 336 | |
| 337 | /** |
| 338 | * Converts the <code>toApi</code>.txt file (or current.txt if not explicitly |
| 339 | * defined using -PtoAPi=<file>) to XML format for use by JDiff. |
| 340 | */ |
| 341 | task newApiXml(type: ApiXmlConversionTask, dependsOn: configurations.doclava) { |
| 342 | classpath configurations.doclava.resolve() |
| 343 | |
| 344 | if (project.hasProperty("toApi")) { |
| 345 | // Use an explicit API file. |
| 346 | inputApiFile = new File(rootProject.ext.supportRootFolder, "api/${toApi}.txt") |
| 347 | } else { |
| 348 | // Use the current API file (e.g. current.txt). |
| 349 | inputApiFile = generateApi.apiFile |
| 350 | dependsOn generateApi |
| 351 | } |
| 352 | |
| 353 | int lastDot = inputApiFile.name.lastIndexOf('.') |
| 354 | outputApiXmlFile = new File(project.docsDir, |
| 355 | "release/" + inputApiFile.name.substring(0, lastDot) + ".xml") |
| 356 | } |
| 357 | |
| 358 | /** |
| 359 | * Converts the <code>fromApi</code>.txt file (or the most recently released |
| 360 | * X.Y.Z.txt if not explicitly defined using -PfromAPi=<file>) to XML format |
| 361 | * for use by JDiff. |
| 362 | */ |
| 363 | task oldApiXml(type: ApiXmlConversionTask, dependsOn: configurations.doclava) { |
| 364 | classpath configurations.doclava.resolve() |
| 365 | |
| 366 | if (project.hasProperty("fromApi")) { |
| 367 | // Use an explicit API file. |
| 368 | inputApiFile = new File(rootProject.ext.supportRootFolder, "api/${fromApi}.txt") |
| 369 | } else if (project.hasProperty("toApi") && toApi.matches(~/(\d+\.){2}\d+/)) { |
| 370 | // If toApi matches released API (X.Y.Z) format, use the most recently |
| 371 | // released API file prior to toApi. |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 372 | inputApiFile = getApiFile(toApi, true) |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 373 | } else { |
| 374 | // Use the most recently released API file. |
Alan Viverette | b0ba50d | 2017-03-06 13:03:25 -0500 | [diff] [blame] | 375 | inputApiFile = getApiFile(); |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 376 | } |
| 377 | |
| 378 | int lastDot = inputApiFile.name.lastIndexOf('.') |
| 379 | outputApiXmlFile = new File(project.docsDir, |
| 380 | "release/" + inputApiFile.name.substring(0, lastDot) + ".xml") |
| 381 | } |
| 382 | |
| 383 | /** |
| 384 | * Generates API diffs. |
| 385 | * <p> |
| 386 | * By default, diffs are generated for the delta between current.txt and the |
| 387 | * next most recent X.Y.Z.txt API file. Behavior may be changed by specifying |
| 388 | * one or both of -PtoApi and -PfromApi. |
| 389 | * <p> |
| 390 | * If both fromApi and toApi are specified, diffs will be generated for |
| 391 | * fromApi -> toApi. For example, 25.0.0 -> 26.0.0 diffs could be generated by |
| 392 | * using: |
| 393 | * <br><code> |
| 394 | * ./gradlew generateDiffs -PfromApi=25.0.0 -PtoApi=26.0.0 |
| 395 | * </code> |
| 396 | * <p> |
| 397 | * If only toApi is specified, it MUST be specified as X.Y.Z and diffs will be |
| 398 | * generated for (release before toApi) -> toApi. For example, 24.2.0 -> 25.0.0 |
| 399 | * diffs could be generated by using: |
| 400 | * <br><code> |
| 401 | * ./gradlew generateDiffs -PtoApi=25.0.0 |
| 402 | * </code> |
| 403 | * <p> |
| 404 | * If only fromApi is specified, diffs will be generated for fromApi -> current. |
| 405 | * For example, lastApiReview -> current diffs could be generated by using: |
| 406 | * <br><code> |
| 407 | * ./gradlew generateDiffs -PfromApi=lastApiReview |
| 408 | * </code> |
| 409 | * <p> |
| 410 | */ |
| 411 | task generateDiffs(type: JDiffTask, dependsOn: [configurations.jdiff, configurations.doclava, |
| 412 | oldApiXml, newApiXml, generateDocs]) { |
| 413 | // Base classpath is Android SDK, sub-projects add their own. |
| 414 | classpath = project.ext.androidJar |
| 415 | |
| 416 | // JDiff properties. |
| 417 | oldApiXmlFile = oldApiXml.outputApiXmlFile |
| 418 | newApiXmlFile = newApiXml.outputApiXmlFile |
| 419 | newJavadocPrefix = "../../../../reference/" |
| 420 | |
| 421 | String newApi = newApiXmlFile.name |
| 422 | int lastDot = newApi.lastIndexOf('.') |
| 423 | newApi = newApi.substring(0, lastDot) |
| 424 | |
| 425 | // Javadoc properties. |
| 426 | docletpath = configurations.jdiff.resolve() |
| 427 | destinationDir = new File(project.docsDir, "online/sdk/support_api_diff/$newApi") |
| 428 | title = "Support Library API Differences Report" |
| 429 | |
| 430 | exclude '**/BuildConfig.java' |
| 431 | exclude '**/R.java' |
| 432 | } |
| 433 | |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 434 | // configuration file for setting up api diffs and api docs |
| 435 | void registerForDocsTask(Task task, Project subProject, releaseVariant) { |
| 436 | task.dependsOn releaseVariant.javaCompile |
| 437 | task.source { |
| 438 | def buildConfig = fileTree(releaseVariant.getGenerateBuildConfig().sourceOutputDir) |
| 439 | return releaseVariant.javaCompile.source.minus(buildConfig) + |
| 440 | fileTree(releaseVariant.aidlCompile.sourceOutputDir) + |
| 441 | fileTree(releaseVariant.outputs[0].processResources.sourceOutputDir) |
| 442 | } |
Yigit Boyar | 2bf30b6 | 2017-04-12 14:17:01 -0700 | [diff] [blame] | 443 | |
Aurimas Liutikas | 95d5e70 | 2017-06-28 10:39:39 -0700 | [diff] [blame] | 444 | task.classpath += releaseVariant.getCompileClasspath(null) + |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 445 | files(releaseVariant.javaCompile.destinationDir) |
| 446 | } |
| 447 | |
| 448 | // configuration file for setting up api diffs and api docs |
| 449 | void registerJavaProjectForDocsTask(Task task, Project subProject, javaCompileTask) { |
| 450 | task.dependsOn javaCompileTask |
| 451 | task.source javaCompileTask.source |
| 452 | task.classpath += files(javaCompileTask.classpath) + |
| 453 | files(javaCompileTask.destinationDir) |
| 454 | } |
| 455 | |
| 456 | subprojects { subProject -> |
| 457 | subProject.afterEvaluate { p -> |
Yigit Boyar | 7f3c5ed | 2017-03-07 09:29:16 -0800 | [diff] [blame] | 458 | if (!p.hasProperty("noDocs") || !p.noDocs) { |
| 459 | if (p.hasProperty('android') && p.android.hasProperty('libraryVariants')) { |
| 460 | p.android.libraryVariants.all { v -> |
| 461 | if (v.name == 'release') { |
| 462 | registerForDocsTask(rootProject.generateDocs, p, v) |
| 463 | registerForDocsTask(rootProject.generateApi, p, v) |
| 464 | registerForDocsTask(rootProject.generateDiffs, p, v) |
| 465 | } |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 466 | } |
Yigit Boyar | 7f3c5ed | 2017-03-07 09:29:16 -0800 | [diff] [blame] | 467 | } else if (p.hasProperty("compileJava")) { |
| 468 | registerJavaProjectForDocsTask(rootProject.generateDocs, p, p.compileJava) |
Yigit Boyar | 88c16ce | 2017-02-08 16:06:14 -0800 | [diff] [blame] | 469 | } |
| 470 | } |
| 471 | } |
Alan Viverette | d953ba0 | 2017-02-23 14:39:17 -0500 | [diff] [blame] | 472 | } |