From 3d4d87f57ad57a701d263d1996f34e379a0d4fe5 Mon Sep 17 00:00:00 2001 From: Peter Wen Date: Mon, 7 Jun 2021 12:15:06 +0000 Subject: [PATCH] Android: Institute groovy style and lint guidance Add the groovy style guide and linter resources (for VS code) to //third_party/android_deps/README.md since this is the only project we have that contains groovy code. Re-format and fix our existing code to be more idiomatic and to conform to the groovy code linter with the settings listed in the README.md. This allows us to automatically fix-up style issues locally as we work on the code, and also makes it easier for new code to be more idomatic when extending our existing code. Bug: 1216032 Cq-Include-Trybots: luci.chromium.try:3pp-linux-amd64-packager Change-Id: Ie9a848912d69233df462f749406765622ddeb3ad Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2940907 Reviewed-by: Mohamed Heikal Commit-Queue: Peter Wen Cr-Commit-Position: refs/heads/master@{#889727} --- third_party/android_deps/README.md | 42 +- .../main/groovy/BuildConfigGenerator.groovy | 1086 ++++++++--------- .../src/main/groovy/ChromiumDepGraph.groovy | 970 +++++++-------- .../src/main/groovy/ChromiumPlugin.groovy | 42 +- 4 files changed, 1068 insertions(+), 1072 deletions(-) diff --git a/third_party/android_deps/README.md b/third_party/android_deps/README.md index b8dda00d1efc76..2cf47772e83179 100644 --- a/third_party/android_deps/README.md +++ b/third_party/android_deps/README.md @@ -71,4 +71,44 @@ features. An alternative way to implement it is to mix gradle to purely fetch dependencies and their pom.xml files, and use Python to process and generate the files. This approach was not as successful, as some information about the dependencies does not seem to be available purely from the POM file, which -resulted in expecting dependencies that gradle considered unnecessary. +resulted in expecting dependencies that gradle considered unnecessary. This is +especially true nowadays that pom.xml files for many dependencies are no longer +maintained by the package authors. + +#### Groovy Style Guide +The groovy code in `//third_party/android_deps/buildSrc/src/main/groovy` loosely +follows the [Groovy Style Guide][groovy_style_guide], and can be linted by each +dev with [npm-groovy-lint][npm_groovy_lint] via the +[VS Code extension][vs_code_groovy_lint]. The line length limit for groovy is +**120** characters. + +Here is a sample `.groovylintrc.json` file: + +``` +{ + "extends": "recommended", + "rules": { + "CatchException": "off", + "CompileStatic": "off", + "DuplicateMapLiteral": "off", + "DuplicateNumberLiteral": "off", + "DuplicateStringLiteral": "off", + "FactoryMethodName": "off", + "JUnitPublicProperty": "off", + "JavaIoPackageAccess": "off", + "MethodCount": "off", + "NestedForLoop": "off", + "ThrowRuntimeException": "off", + "formatting.Indentation": { + "spacesPerIndentLevel": 4 + } + } +} +``` + +This is a list of rule names: [Groovy Rule Index by Name][groovy_rule_index]. + +[groovy_style_guide]: https://groovy-lang.org/style-guide.html +[npm_groovy_lint]: https://github.com/nvuillam/npm-groovy-lint +[vs_code_groovy_lint]: https://marketplace.visualstudio.com/items?itemName=NicolasVuillamy.vscode-groovy-lint +[groovy_rule_index]: https://codenarc.org/codenarc-rule-index-by-name.html diff --git a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy index f7c0fb16e6b713..90514f0a2516fa 100644 --- a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy +++ b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy @@ -3,6 +3,7 @@ // found in the LICENSE file. import groovy.json.JsonOutput +import groovy.text.SimpleTemplateEngine import groovy.text.Template import groovy.transform.SourceURI import org.gradle.api.DefaultTask @@ -12,17 +13,15 @@ import org.gradle.api.tasks.TaskAction import java.nio.file.Path import java.nio.file.Paths -import java.time.LocalDate import java.util.concurrent.Executors import java.util.concurrent.ExecutorService import java.util.concurrent.Future -import java.util.concurrent.TimeUnit import java.util.regex.Pattern +import java.util.regex.Matcher /** - * Task to download dependencies specified in {@link ChromiumPlugin} and configure the - * Chromium build to integrate them. Used by declaring a new task in a {@code build.gradle} - * file: + * Task to download dependencies specified in {@link ChromiumPlugin} and configure the Chromium build to integrate them. + * Used by declaring a new task in a {@code build.gradle} file: *
  * task myTaskName(type: BuildConfigGenerator) {
  *   repositoryPath 'build_files_and_repository_location/'
@@ -30,47 +29,47 @@ import java.util.regex.Pattern
  * 
*/ class BuildConfigGenerator extends DefaultTask { - private static final BUILD_GN_TOKEN_START = "# === Generated Code Start ===" - private static final BUILD_GN_TOKEN_END = "# === Generated Code End ===" - private static final BUILD_GN_GEN_PATTERN = Pattern.compile( - "${BUILD_GN_TOKEN_START}(.*)${BUILD_GN_TOKEN_END}", - Pattern.DOTALL) - private static final GEN_REMINDER = "# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.\n" - private static final DEPS_TOKEN_START = "# === ANDROID_DEPS Generated Code Start ===" - private static final DEPS_TOKEN_END = "# === ANDROID_DEPS Generated Code End ===" - private static final DEPS_GEN_PATTERN = Pattern.compile( - "${DEPS_TOKEN_START}(.*)${DEPS_TOKEN_END}", - Pattern.DOTALL) - private static final DOWNLOAD_DIRECTORY_NAME = "libs" - // The 3pp bot now adds an epoch to the version tag, this needs to be kept - // in sync with 3pp epoch at: + + private static final String BUILD_GN_TOKEN_START = '# === Generated Code Start ===' + private static final String BUILD_GN_TOKEN_END = '# === Generated Code End ===' + private static final Pattern BUILD_GN_GEN_PATTERN = Pattern.compile( + "${BUILD_GN_TOKEN_START}(.*)${BUILD_GN_TOKEN_END}", Pattern.DOTALL) + private static final String GEN_REMINDER = + '# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.\n' + private static final String DEPS_TOKEN_START = '# === ANDROID_DEPS Generated Code Start ===' + private static final String DEPS_TOKEN_END = '# === ANDROID_DEPS Generated Code End ===' + private static final Pattern DEPS_GEN_PATTERN = Pattern.compile( + "${DEPS_TOKEN_START}(.*)${DEPS_TOKEN_END}", Pattern.DOTALL) + private static final String DOWNLOAD_DIRECTORY_NAME = 'libs' + // The 3pp bot now adds an epoch to the version tag, this needs to be kept in sync with 3pp epoch at: + /* groovylint-disable-next-line LineLength */ // https://source.chromium.org/chromium/infra/infra/+/master:recipes/recipe_modules/support_3pp/resolved_spec.py?q=symbol:PACKAGE_EPOCH&ss=chromium - private static final THREEPP_EPOCH = "2" + private static final String THREEPP_EPOCH = '2' // Some libraries are hosted in Chromium's //third_party directory. This is a mapping between // them so they can be used instead of android_deps pulling in its own copy. - public static final def EXISTING_LIBS = [ - 'com_ibm_icu_icu4j': '//third_party/icu4j:icu4j_java', - 'com_almworks_sqlite4java_sqlite4java': '//third_party/sqlite4java:sqlite4java_java', - 'com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework': + static final Map EXISTING_LIBS = [ + com_ibm_icu_icu4j: '//third_party/icu4j:icu4j_java', + com_almworks_sqlite4java_sqlite4java: '//third_party/sqlite4java:sqlite4java_java', + com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework: '//third_party/accessibility_test_framework:accessibility_test_framework_java', - 'junit_junit': '//third_party/junit:junit', - 'org_bouncycastle_bcprov_jdk15on': '//third_party/bouncycastle:bouncycastle_java', - 'org_hamcrest_hamcrest_core': '//third_party/hamcrest:hamcrest_core_java', - 'org_hamcrest_hamcrest_integration': '//third_party/hamcrest:hamcrest_integration_java', - 'org_hamcrest_hamcrest_library': '//third_party/hamcrest:hamcrest_library_java', + junit_junit: '//third_party/junit:junit', + org_bouncycastle_bcprov_jdk15on: '//third_party/bouncycastle:bouncycastle_java', + org_hamcrest_hamcrest_core: '//third_party/hamcrest:hamcrest_core_java', + org_hamcrest_hamcrest_integration: '//third_party/hamcrest:hamcrest_integration_java', + org_hamcrest_hamcrest_library: '//third_party/hamcrest:hamcrest_library_java', ] // Prefixes of autorolled libraries in //third_party/android_deps_autorolled. - public static final def AUTOROLLED_LIB_PREFIXES = [ ] + static final List AUTOROLLED_LIB_PREFIXES = [] - public static final def AUTOROLLED_REPO_PATH = 'third_party/android_deps_autorolled' + static final String AUTOROLLED_REPO_PATH = 'third_party/android_deps_autorolled' - public static final def COPYRIGHT_HEADER = """\ + static final String COPYRIGHT_HEADER = '''\ # Copyright 2021 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - """.stripIndent() + '''.stripIndent() /** * Directory where the artifacts will be downloaded and where files will be generated. @@ -81,86 +80,281 @@ class BuildConfigGenerator extends DefaultTask { @Input String repositoryPath - /** - * Relative path to the Chromium source root from the build.gradle file. - */ + /** Relative path to the Chromium source root from the build.gradle file. */ @Input String chromiumSourceRoot - /** - * Name of the cipd root package. - */ + /** Name of the cipd root package. */ @Input String cipdBucket - /** - * Skips license file import. - */ + /** Skips license file import. */ @Input boolean skipLicenses - /** - * Array with visibility for targets which are not listed in build.gradle - */ + /** Array with visibility for targets which are not listed in build.gradle */ @Input String[] internalTargetVisibility - /** - * Whether to ignore DEPS file. - */ + /** Whether to ignore DEPS file. */ @Input boolean ignoreDEPS - /** - * The URI of the file BuildConfigGenerator.groovy - */ + /** The URI of the file BuildConfigGenerator.groovy */ @Input @SourceURI URI sourceUri + static String translateTargetName(String targetName) { + if (isPlayServicesTarget(targetName)) { + return targetName.replaceFirst('com_', '').replaceFirst('android_gms_', '') + } + return targetName + } + + static boolean isPlayServicesTarget(String dependencyId) { + // Firebase has historically been treated as a part of play services, so it counts here for backwards + // compatibility. Datatransport is new as of 2019 and is used by many play services libraries. + return Pattern.matches('.*google.*(play_services|firebase|datatransport).*', dependencyId) + } + + static String makeOwners() { + // Make it easier to upgrade existing dependencies without full third_party review. + return 'file://third_party/android_deps/OWNERS\n' + } + + static String makeReadme(ChromiumDepGraph.DependencyDescription dependency) { + List licenseStrings = [] + for (ChromiumDepGraph.LicenseSpec license : dependency.licenses) { + // Replace license names with ones that are whitelisted, see third_party/PRESUBMIT.py + switch (license.name) { + case 'The Apache License, Version 2.0': + case 'The Apache Software License, Version 2.0': + licenseStrings.add('Apache Version 2.0') + break + case 'GNU General Public License, version 2, with the Classpath Exception': + licenseStrings.add('GPL v2 with the classpath exception') + break + default: + licenseStrings.add(license.name) + } + } + String licenseString = String.join(', ', licenseStrings) + + boolean securityCritical = dependency.supportsAndroid && dependency.isShipped + String licenseFile = dependency.isShipped ? 'LICENSE' : 'NOT_SHIPPED' + + return """\ + Name: ${dependency.displayName} + Short Name: ${dependency.name} + URL: ${dependency.url} + Version: ${dependency.version} + License: ${licenseString} + License File: ${licenseFile} + Security Critical: ${securityCritical ? 'yes' : 'no'} + ${dependency.licenseAndroidCompatible ? 'License Android Compatible: yes' : ''} + Description: + ${dependency.description} + + Local Modifications: + No modifications. + """.stripIndent() + } + + static String makeCipdYaml(ChromiumDepGraph.DependencyDescription dependency, String cipdBucket, String repoPath) { + String cipdVersion = "${THREEPP_EPOCH}@${dependency.version}.${dependency.cipdSuffix}" + String cipdPath = "${cipdBucket}/${repoPath}" + // CIPD does not allow uppercase in names. + cipdPath += "/${DOWNLOAD_DIRECTORY_NAME}/$dependency.directoryName" + + // NOTE: The fetch_all.py script relies on the format of this file! See fetch_all.py:GetCipdPackageInfo(). + // NOTE: Keep the copyright year 2018 until this generated code is updated, avoiding annual churn of all + // cipd.yaml files. + return """\ + # Copyright 2018 The Chromium Authors. All rights reserved. + # Use of this source code is governed by a BSD-style license that can be + # found in the LICENSE file. + + # To create CIPD package run the following command. + # cipd create --pkg-def cipd.yaml -tag version:${cipdVersion} + package: ${cipdPath} + description: "${dependency.displayName}" + data: + - file: ${dependency.fileName} + """.stripIndent() + } + + static void validateLicenses(ChromiumDepGraph.DependencyDescription dependency) { + if (dependency.licenses.empty) { + throw new RuntimeException("Missing license for ${dependency.id}.") + } + + for (ChromiumDepGraph.LicenseSpec license : dependency.licenses) { + if (!license.path?.trim() && !license.url?.trim()) { + throw new RuntimeException("Missing license for ${dependency.id}. License Name was: ${license.name}") + } + } + } + + static void downloadLicenses(ChromiumDepGraph.DependencyDescription dependency, + String normalisedRepoPath, + ExecutorService downloadExecutor, + List downloadTasks) { + String depDir = "$normalisedRepoPath/${computeDepDir(dependency)}" + for (int i = 0; i < dependency.licenses.size(); ++i) { + ChromiumDepGraph.LicenseSpec license = dependency.licenses[i] + if (!license.path?.trim() && license.url?.trim()) { + String destFileSuffix = (dependency.licenses.size() > 1) ? "${i + 1}.tmp" : '' + File destFile = new File("${depDir}/LICENSE${destFileSuffix}") + downloadTasks.add(downloadExecutor.submit { + downloadFile(dependency.id, license.url, destFile) + if (destFile.text.contains(' + try { + out << connectAndFollowRedirects(id, sourceUrl).inputStream + } catch (Exception e) { + throw new RuntimeException("Failed to fetch license for $id url: $sourceUrl", e) + } + } + } + @TaskAction void main() { // Do not run task on subprojects. - if (project != project.getRootProject()) return + if (project != project.rootProject) { + return + } - skipLicenses = skipLicenses || project.hasProperty("skipLicenses") + skipLicenses = skipLicenses ?: project.hasProperty('skipLicenses') Path fetchTemplatePath = Paths.get(sourceUri).resolveSibling('3ppFetch.template') - def fetchTemplate = new groovy.text.SimpleTemplateEngine() - .createTemplate(fetchTemplatePath.toFile()) + Template fetchTemplate = new SimpleTemplateEngine().createTemplate(fetchTemplatePath.toFile()) - def subprojects = new HashSet() + Set subprojects = [] as Set subprojects.add(project) subprojects.addAll(project.subprojects) - def graph = new ChromiumDepGraph(projects: subprojects, logger: project.logger, - skipLicenses: skipLicenses) - def normalisedRepoPath = normalisePath(repositoryPath) + ChromiumDepGraph graph = new ChromiumDepGraph( + projects: subprojects, logger: project.logger, skipLicenses: skipLicenses) + String normalisedRepoPath = normalisePath(repositoryPath) // 1. Parse the dependency data graph.collectDependencies() // 2. Import artifacts into the local repository - def dependencyDirectories = [] - def downloadExecutor = Executors.newCachedThreadPool() - def downloadTasks = [] - def mergeLicensesDeps = [] + List dependencyDirectories = [] + ExecutorService downloadExecutor = Executors.newCachedThreadPool() + List downloadTasks = [] + List mergeLicensesDeps = [] graph.dependencies.values().each { dependency -> if (excludeDependency(dependency) || computeJavaGroupForwardingTarget(dependency) != null) { return } - def dependencyForLogging = dependency.clone() + ChromiumDepGraph.DependencyDescription dependencyForLogging = dependency.clone() // jsonDump() throws StackOverflowError for ResolvedArtifact. dependencyForLogging.artifact = null logger.debug "Processing ${dependency.name}: \n${jsonDump(dependencyForLogging)}" - def depDir = computeDepDir(dependency) - def absoluteDepDir = "${normalisedRepoPath}/${depDir}" + String depDir = computeDepDir(dependency) + String absoluteDepDir = "${normalisedRepoPath}/${depDir}" dependencyDirectories.add(depDir) if (new File("${absoluteDepDir}/${dependency.fileName}").exists()) { - getLogger().quiet("${dependency.id} exists, skipping.") + logger.quiet("${dependency.id} exists, skipping.") return } @@ -170,25 +364,21 @@ class BuildConfigGenerator extends DefaultTask { } new File("${absoluteDepDir}/README.chromium").write(makeReadme(dependency)) - new File("${absoluteDepDir}/cipd.yaml").write(makeCipdYaml(dependency, cipdBucket, - repositoryPath)) + new File("${absoluteDepDir}/cipd.yaml").write(makeCipdYaml(dependency, cipdBucket, repositoryPath)) new File("${absoluteDepDir}/OWNERS").write(makeOwners()) // Enable 3pp flow for //third_party/android_deps only. // TODO(crbug.com/1132368): Enable 3pp flow for subprojects as well. - if (repositoryPath == "third_party/android_deps") { + if (repositoryPath == 'third_party/android_deps') { if (dependency.fileUrl) { - def absoluteDep3ppDir = "${absoluteDepDir}/3pp" + String absoluteDep3ppDir = "${absoluteDepDir}/3pp" new File(absoluteDep3ppDir).mkdirs() - new File("${absoluteDep3ppDir}/3pp.pb").write(make3ppPb(dependency, - cipdBucket, - repositoryPath)) - def fetchFile = new File("${absoluteDep3ppDir}/fetch.py") + new File("${absoluteDep3ppDir}/3pp.pb").write(make3ppPb(dependency, cipdBucket, repositoryPath)) + File fetchFile = new File("${absoluteDep3ppDir}/fetch.py") fetchFile.write(make3ppFetch(fetchTemplate, dependency)) fetchFile.setExecutable(true, false) } else { - throw new RuntimeException("Failed to generate 3pp files for ${dependency.id} " - + "due to empty file url.") + throw new RuntimeException("Failed to generate 3pp files for ${dependency.id} with empty fileUrl.") } } @@ -200,7 +390,7 @@ class BuildConfigGenerator extends DefaultTask { } downloadExecutor.shutdown() // Check for exceptions. - for (def task : downloadTasks) { + for (Future task : downloadTasks) { task.get() } @@ -214,153 +404,97 @@ class BuildConfigGenerator extends DefaultTask { updateDepsDeclaration(graph, cipdBucket, repositoryPath, "${normalisedRepoPath}/../../DEPS") } - dependencyDirectories.sort { path1, path2 -> return path1.compareTo(path2) } + dependencyDirectories.sort { path1, path2 -> return path1 <=> path2 } updateReadmeReferenceFile(dependencyDirectories, "${normalisedRepoPath}/additional_readme_paths.json") } - private static String computeDepDir(ChromiumDepGraph.DependencyDescription dependency) { - return "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" - } - - private void updateBuildTargetDeclaration(ChromiumDepGraph depGraph, String normalisedRepoPath) { - File buildFile = new File("${normalisedRepoPath}/BUILD.gn"); - def sb = new StringBuilder() - - // Comparator to sort the dependency in alphabetical order, with the visible ones coming - // before all the internal ones. - def dependencyComparator = { dependency1, dependency2 -> - def visibilityResult = Boolean.compare(dependency1.visible, dependency2.visible) - if (visibilityResult != 0) return -visibilityResult - - return dependency1.id.compareTo(dependency2.id) - } - - def fixedDependencies = depGraph.dependencies.values().findAll { - dependency -> dependency.usedInBuild - } - fixedDependencies.sort(dependencyComparator).each { dependency -> - appendBuildTarget(dependency, depGraph.dependencies, sb) - } - - sb.append("if (!limit_android_deps) {\n") - def buildWithChromiumDependencies = depGraph.dependencies.values().findAll { - dependency -> !dependency.usedInBuild - } - buildWithChromiumDependencies.sort(dependencyComparator).each { dependency -> - appendBuildTarget(dependency, depGraph.dependencies, sb) - } - sb.append("}\n") - - def out = "${BUILD_GN_TOKEN_START}\n${sb.toString()}\n${BUILD_GN_TOKEN_END}" - if (buildFile.exists()) { - def matcher = BUILD_GN_GEN_PATTERN.matcher(buildFile.getText()) - if (!matcher.find()) throw new IllegalStateException("BUILD.gn insertion point not found.") - out = matcher.replaceFirst(out) - } else { - out = "import(\"//build/config/android/rules.gni\")\n" + out - } - buildFile.write(out) - } - - public void appendBuildTarget(ChromiumDepGraph.DependencyDescription dependency, - Map allDependencies, - StringBuilder sb) { + void appendBuildTarget(ChromiumDepGraph.DependencyDescription dependency, + Map allDependencies, + StringBuilder sb) { if (excludeDependency(dependency) || !dependency.generateTarget) { return } - def targetName = translateTargetName(dependency.id) + "_java" - def javaGroupTarget = computeJavaGroupForwardingTarget(dependency) + String targetName = translateTargetName(dependency.id) + '_java' + String javaGroupTarget = computeJavaGroupForwardingTarget(dependency) if (javaGroupTarget != null) { assert dependency.extension == 'jar' || dependency.extension == 'aar' sb.append(""" - java_group("${targetName}") { - deps = [ \"${javaGroupTarget}\" ] - """.stripIndent()) - if (dependency.testOnly) sb.append(" testonly = true\n") - sb.append("}\n\n") + java_group("${targetName}") { + deps = [ "${javaGroupTarget}" ] + """.stripIndent()) + if (dependency.testOnly) { + sb.append(' testonly = true\n') + } + sb.append('}\n\n') return - } - - def depsStr = "" - if (!dependency.children.isEmpty()) { - dependency.children.each { childDep -> - def dep = allDependencies[childDep] - if (dep.exclude) { - return - } - // Special case: If a child dependency is an existing lib, rather than skipping - // it, replace the child dependency with the existing lib. - def existingLib = EXISTING_LIBS.get(dep.id) - def depTargetName = translateTargetName(dep.id) + "_java" - if (existingLib != null) { - depsStr += "\"${existingLib}\"," - } else if (excludeDependency(dep)) { - def thirdPartyDir = (dep.id.startsWith("androidx")) ? "androidx" : "android_deps" - depsStr += "\"//third_party/${thirdPartyDir}:${depTargetName}\"," - } else if (dep.id == "com_google_android_material_material") { - // Material design is pulled in via doubledown, should - // use the variable instead of the real target. - depsStr += "\"\\\$material_design_target\"," - } else { - depsStr += "\":${depTargetName}\"," } + + String depsStr = '' + dependency.children?.each { childDep -> + ChromiumDepGraph.DependencyDescription dep = allDependencies[childDep] + if (dep.exclude) { + return + } + // Special case: If a child dependency is an existing lib, rather than skipping + // it, replace the child dependency with the existing lib. + String existingLib = EXISTING_LIBS.get(dep.id) + String depTargetName = translateTargetName(dep.id) + '_java' + if (existingLib) { + depsStr += "\"${existingLib}\"," + } else if (excludeDependency(dep)) { + String thirdPartyDir = (dep.id.startsWith('androidx')) ? 'androidx' : 'android_deps' + depsStr += "\"//third_party/${thirdPartyDir}:${depTargetName}\"," + } else if (dep.id == 'com_google_android_material_material') { + // Material design is pulled in via doubledown, should + // use the variable instead of the real target. + depsStr += '"$material_design_target",' + } else { + depsStr += "\":${depTargetName}\"," } } - def libPath = "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" + String libPath = "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" sb.append(GEN_REMINDER) if (dependency.extension == 'jar') { sb.append("""\ - java_prebuilt("${targetName}") { - jar_path = "${libPath}/${dependency.fileName}" - output_name = "${dependency.id}" - """.stripIndent()) + java_prebuilt("${targetName}") { + jar_path = "${libPath}/${dependency.fileName}" + output_name = "${dependency.id}" + """.stripIndent()) if (dependency.supportsAndroid) { - sb.append(" supports_android = true\n") + sb.append(' supports_android = true\n') } else { - // Save some time by not validating classpaths of desktop - // .jars. Also required to break a dependency cycle for - // errorprone. - sb.append(" enable_bytecode_checks = false\n") + // Save some time by not validating classpaths of desktop .jars. Also required to break a dependency + // cycle for errorprone. + sb.append(' enable_bytecode_checks = false\n') } } else if (dependency.extension == 'aar') { sb.append("""\ - android_aar_prebuilt("${targetName}") { - aar_path = "${libPath}/${dependency.fileName}" - info_path = "${libPath}/${dependency.id}.info" + android_aar_prebuilt("${targetName}") { + aar_path = "${libPath}/${dependency.fileName}" + info_path = "${libPath}/${dependency.id}.info" """.stripIndent()) } else { - throw new IllegalStateException("Dependency type should be JAR or AAR") - } + throw new IllegalStateException('Dependency type should be JAR or AAR') + } sb.append(generateBuildTargetVisibilityDeclaration(dependency)) - if (dependency.testOnly) sb.append(" testonly = true\n") - if (!depsStr.empty) sb.append(" deps = [${depsStr}]\n") - addSpecialTreatment(sb, dependency.id, dependency.extension) - - sb.append("}\n\n") - } - - public static String translateTargetName(String targetName) { - if (isPlayServicesTarget(targetName)) { - return targetName.replaceFirst("com_", "").replaceFirst("android_gms_", "") + if (dependency.testOnly) { + sb.append(' testonly = true\n') } - return targetName - } + if (!depsStr.empty) { + sb.append(" deps = [${depsStr}]\n") + } + addSpecialTreatment(sb, dependency.id, dependency.extension) - public static boolean isPlayServicesTarget(String dependencyId) { - // Firebase has historically been treated as a part of play services, so it counts here for - // backwards compatibility. Datatransport is new as of 2019 and is used by many play - // services libraries. - return Pattern.matches(".*google.*(play_services|firebase|datatransport).*", dependencyId) - } + sb.append('}\n\n') + } - public String generateBuildTargetVisibilityDeclaration( - ChromiumDepGraph.DependencyDescription dependency) { - def sb = new StringBuilder() + String generateBuildTargetVisibilityDeclaration(ChromiumDepGraph.DependencyDescription dependency) { + StringBuilder sb = new StringBuilder() switch (dependency.id) { case 'com_google_android_material_material': sb.append(' # Material Design is pulled in via Doubledown, thus this target should not\n') @@ -382,10 +516,56 @@ class BuildConfigGenerator extends DefaultTask { return sb.toString() } - private String generateInternalTargetVisibilityLine() { - return 'visibility = ' + makeGnArray(internalTargetVisibility) + '\n' + boolean excludeDependency(ChromiumDepGraph.DependencyDescription dependency) { + if (dependency.exclude || EXISTING_LIBS.get(dependency.id)) { + return true + } + boolean isAndroidxRepository = (repositoryPath == 'third_party/androidx') + boolean isAndroidxDependency = (dependency.id.startsWith('androidx')) + if (isAndroidxRepository != isAndroidxDependency) { + return true + } + if (repositoryPath == AUTOROLLED_REPO_PATH) { + String targetName = translateTargetName(dependency.id) + '_java' + return !isTargetAutorolled(targetName) + } + // TODO(crbug.com/1184780): Remove this once org_robolectric_shadows_multidex is updated to a newer version + // which does not need jetify. + if (dependency.directoryName == 'org_robolectric_shadows_multidex') { + if (dependency.version != '4.3.1') { + throw new RuntimeException('Got a new version for org_robolectric_shadows_multidex. If this new ' + + "version doesn't need jetify, please move this dependency back to the " + + 'auto-generated section in //DEPS and //third_party/android_deps/BUILD.gn.') + } + return true + } + return false + } + + /** If |dependency| should be a java_group(), returns target to forward to. Returns null otherwise. */ + String computeJavaGroupForwardingTarget(ChromiumDepGraph.DependencyDescription dependency) { + String targetName = translateTargetName(dependency.id) + '_java' + return repositoryPath != AUTOROLLED_REPO_PATH && isTargetAutorolled(targetName) ? + "//${AUTOROLLED_REPO_PATH}:${targetName}" : null + } + + private static String makeGnArray(String[] values) { + StringBuilder sb = new StringBuilder() + sb.append('[') + for (String value : values) { + sb.append('"') + sb.append(value) + sb.append('",') + } + sb.replace(sb.length() - 1, sb.length(), ']') + return sb.toString() + } + + private static String computeDepDir(ChromiumDepGraph.DependencyDescription dependency) { + return "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" } + /* groovylint-disable-next-line MethodSize */ private static void addSpecialTreatment(StringBuilder sb, String dependencyId, String dependencyExtension) { addPreconditionsOverrideTreatment(sb, dependencyId) @@ -394,38 +574,41 @@ class BuildConfigGenerator extends DefaultTask { // accessibility_test_framework_java which requires_android. sb.append(' bypass_platform_checks = true\n') } - if (dependencyExtension == "aar" && - (dependencyId.startsWith('androidx') || - dependencyId.startsWith('com_android_support'))) { - // androidx and com_android_support libraries have duplicate resources such as 'primary_text_default_material_dark'. - sb.append(' resource_overlay = true\n') - } - switch(dependencyId) { + if (dependencyExtension == 'aar' && + (dependencyId.startsWith('androidx') || dependencyId.startsWith('com_android_support'))) { + // The androidx and com_android_support libraries have duplicate resources such as + // 'primary_text_default_material_dark'. + sb.append(' resource_overlay = true\n') + } + + switch (dependencyId) { case 'androidx_annotation_annotation': sb.append(' # https://crbug.com/989505\n') sb.append(' jar_excluded_patterns = ["META-INF/proguard/*"]\n') break case 'androidx_annotation_annotation_experimental': - sb.append("""\ + sb.append('''\ | # https://crbug.com/1213876 | deps = | [ "//third_party/android_deps:org_jetbrains_kotlin_kotlin_stdlib_java" ] - |""".stripMargin()) + |'''.stripMargin()) break case 'androidx_core_core': - sb.append('\n') - sb.append(' # Target has AIDL, but we do not support it yet: http://crbug.com/644439\n') - sb.append(' ignore_aidl = true\n') - sb.append('\n') - sb.append(' # Manifest and proguard config have just one entry: Adding (and -keep\'ing\n') - sb.append(' # android:appComponentFactory="androidx.core.app.CoreComponentFactory"\n') - sb.append(' # Chrome does not use this feature and it causes a scary stack trace to be\n') - sb.append(' # shown when incremental_install=true.\n') - sb.append(' ignore_manifest = true\n') - sb.append(' ignore_proguard_configs = true\n') + sb.with { + append('\n') + append(' # Target has AIDL, but we do not support it yet: http://crbug.com/644439\n') + append(' ignore_aidl = true\n') + append('\n') + append(' # Manifest and proguard config have just one entry: Adding (and -keep\'ing\n') + append(' # android:appComponentFactory="androidx.core.app.CoreComponentFactory"\n') + append(' # Chrome does not use this feature and it causes a scary stack trace to be\n') + append(' # shown when incremental_install=true.\n') + append(' ignore_manifest = true\n') + append(' ignore_proguard_configs = true\n') + } break case 'androidx_fragment_fragment': - sb.append("""\ + sb.append('''\ | deps += [ | "//third_party/android_deps/utils:java", | ] @@ -434,7 +617,7 @@ class BuildConfigGenerator extends DefaultTask { | use_classic_desugar = true | | bytecode_rewriter_target = "//build/android/bytecode:fragment_activity_replacer" - |""".stripMargin()) + |'''.stripMargin()) break case 'androidx_media_media': case 'androidx_versionedparcelable_versionedparcelable': @@ -486,7 +669,7 @@ class BuildConfigGenerator extends DefaultTask { // Necessary to not have duplicate classes after jetification. // They can be removed when we no longer jetify targets // that depend on com_android_support_support_compat. - sb.append("""\ + sb.append('''\ | jar_excluded_patterns = [ | "android/support/v4/graphics/drawable/IconCompatParcelizer.class", | "android/support/v4/os/ResultReceiver*", @@ -496,7 +679,7 @@ class BuildConfigGenerator extends DefaultTask { | "android/support/v4/os/IResultReceiver*", | ] | - |""".stripMargin()) + |'''.stripMargin()) break case 'com_android_support_transition': // Not specified in the POM, compileOnly dependency not supposed to be used unless @@ -510,13 +693,13 @@ class BuildConfigGenerator extends DefaultTask { // Necessary to not have identical classes after jetification. // They can be removed when we no longer jetify targets // that depend on com_android_support_versionedparcelable. - sb.append("""\ + sb.append('''\ | jar_excluded_patterns = [ | "android/support/v4/graphics/drawable/IconCompat.class", | "androidx/*", | ] | - |""".stripMargin()) + |'''.stripMargin()) break case 'com_google_ar_core': // Target .aar file contains .so libraries that need to be extracted, @@ -531,12 +714,14 @@ class BuildConfigGenerator extends DefaultTask { sb.append(' jar_excluded_patterns = ["*/ListenableFuture.class"]\n') break case 'com_google_guava_guava_android': - sb.append('\n') - sb.append(' # Add a dep to com_google_guava_listenablefuture_java\n') - sb.append(' # because androidx_concurrent_futures also depends on it and to avoid\n') - sb.append(' # defining ListenableFuture.class twice.\n') - sb.append(' deps += [":com_google_guava_listenablefuture_java"]\n') - sb.append(' jar_excluded_patterns += ["*/ListenableFuture.class"]\n') + sb.with { + append('\n') + append(' # Add a dep to com_google_guava_listenablefuture_java\n') + append(' # because androidx_concurrent_futures also depends on it and to avoid\n') + append(' # defining ListenableFuture.class twice.\n') + append(' deps += [":com_google_guava_listenablefuture_java"]\n') + append(' jar_excluded_patterns += ["*/ListenableFuture.class"]\n') + } break case 'com_google_code_findbugs_jsr305': case 'com_google_errorprone_error_prone_annotations': @@ -551,14 +736,14 @@ class BuildConfigGenerator extends DefaultTask { break case 'androidx_test_rules': // Target needs Android SDK deps which exist in third_party/android_sdk. - sb.append("""\ + sb.append('''\ | deps += [ | "//third_party/android_sdk:android_test_base_java", | "//third_party/android_sdk:android_test_mock_java", | "//third_party/android_sdk:android_test_runner_java", | ] | - |""".stripMargin()) + |'''.stripMargin()) break case 'androidx_test_espresso_espresso_contrib': case 'androidx_test_espresso_espresso_web': @@ -570,9 +755,9 @@ class BuildConfigGenerator extends DefaultTask { sb.append(' jar_excluded_patterns = [ "*xmlpull*" ]\n') break case 'androidx_preference_preference': - sb.append("""\ + sb.append('''\ | bytecode_rewriter_target = "//build/android/bytecode:fragment_activity_replacer" - |""".stripMargin()) + |'''.stripMargin()) // Replace broad library -keep rules with a more limited set in // chrome/android/java/proguard.flags instead. sb.append(' ignore_proguard_configs = true\n') @@ -592,25 +777,27 @@ class BuildConfigGenerator extends DefaultTask { sb.append(' ignore_manifest = true\n') break case 'com_google_protobuf_protobuf_javalite': - sb.append(' # Prebuilt protos in the runtime library.\n') - sb.append(' # If you want to use these protos, you should create a proto_java_library\n') - sb.append(' # target for them. See crbug.com/1103399 for discussion.\n') - sb.append(' jar_excluded_patterns = [\n') - sb.append(' "com/google/protobuf/Any*",\n') - sb.append(' "com/google/protobuf/Api*",\n') - sb.append(' "com/google/protobuf/Duration*",\n') - sb.append(' "com/google/protobuf/Empty*",\n') - sb.append(' "com/google/protobuf/FieldMask*",\n') - sb.append(' "com/google/protobuf/SourceContext*",\n') - sb.append(' "com/google/protobuf/Struct\\\\\\$1.class",\n') - sb.append(' "com/google/protobuf/Struct\\\\\\$Builder.class",\n') - sb.append(' "com/google/protobuf/Struct.class",\n') - sb.append(' "com/google/protobuf/StructOrBuilder.class",\n') - sb.append(' "com/google/protobuf/StructProto.class",\n') - sb.append(' "com/google/protobuf/Timestamp*",\n') - sb.append(' "com/google/protobuf/Type*",\n') - sb.append(' "com/google/protobuf/Wrappers*",\n') - sb.append(' ]') + sb.with { + append(' # Prebuilt protos in the runtime library.\n') + append(' # If you want to use these protos, you should create a proto_java_library\n') + append(' # target for them. See crbug.com/1103399 for discussion.\n') + append(' jar_excluded_patterns = [\n') + append(' "com/google/protobuf/Any*",\n') + append(' "com/google/protobuf/Api*",\n') + append(' "com/google/protobuf/Duration*",\n') + append(' "com/google/protobuf/Empty*",\n') + append(' "com/google/protobuf/FieldMask*",\n') + append(' "com/google/protobuf/SourceContext*",\n') + append(' "com/google/protobuf/Struct\\\\\\$1.class",\n') + append(' "com/google/protobuf/Struct\\\\\\$Builder.class",\n') + append(' "com/google/protobuf/Struct.class",\n') + append(' "com/google/protobuf/StructOrBuilder.class",\n') + append(' "com/google/protobuf/StructProto.class",\n') + append(' "com/google/protobuf/Timestamp*",\n') + append(' "com/google/protobuf/Type*",\n') + append(' "com/google/protobuf/Wrappers*",\n') + append(' ]') + } break case 'androidx_startup_startup_runtime': sb.append(' # Keeps emoji2 code. See http://crbug.com/1205141\n') @@ -658,24 +845,24 @@ class BuildConfigGenerator extends DefaultTask { sb.append(' visibility = [ "//build/android/gyp/resources_shrinker:*" ]\n') break case 'org_jetbrains_kotlinx_kotlinx_coroutines_android': - sb.append('requires_android = true') - break + sb.append('requires_android = true') + break } } private static void addPreconditionsOverrideTreatment(StringBuilder sb, String dependencyId) { - def targetName = translateTargetName(dependencyId) - switch(targetName) { - case "androidx_core_core": - case "com_google_guava_guava_android": - case "google_play_services_basement": - if (targetName == "com_google_guava_guava_android") { - // com_google_guava_guava_android is java_prebuilt(). - sb.append("bypass_platform_checks = true") - } - def libraryDep = "//third_party/android_deps/local_modifications/preconditions:" + - computePreconditionsStubLibraryForDep(dependencyId) - sb.append(""" + String targetName = translateTargetName(dependencyId) + switch (targetName) { + case 'androidx_core_core': + case 'com_google_guava_guava_android': + case 'google_play_services_basement': + if (targetName == 'com_google_guava_guava_android') { + // com_google_guava_guava_android is java_prebuilt(). + sb.append('bypass_platform_checks = true') + } + String libraryDep = '//third_party/android_deps/local_modifications/preconditions:' + + computePreconditionsStubLibraryForDep(dependencyId) + sb.append(""" | | jar_excluded_patterns = [] | if (!enable_java_asserts) { @@ -688,55 +875,107 @@ class BuildConfigGenerator extends DefaultTask { | ] | } |""".stripMargin()) - } + } } private static String computePreconditionsStubLibraryForDep(String dependencyId) { - def targetName = translateTargetName(dependencyId) + String targetName = translateTargetName(dependencyId) switch (targetName) { - case "androidx_core_core": - return "androidx_stub_preconditions_java" - case "com_google_guava_guava_android": - return "guava_stub_preconditions_java" - case "google_play_services_basement": - return "gms_stub_preconditions_java" + case 'androidx_core_core': + return 'androidx_stub_preconditions_java' + case 'com_google_guava_guava_android': + return 'guava_stub_preconditions_java' + case 'google_play_services_basement': + return 'gms_stub_preconditions_java' } return null } private static String computePreconditionsClassForDep(String dependencyId) { - def targetName = translateTargetName(dependencyId) + String targetName = translateTargetName(dependencyId) switch (targetName) { - case "androidx_core_core": - return "androidx/core/util/Preconditions.class" - case "com_google_guava_guava_android": - return "com/google/common/base/Preconditions.class" - case "google_play_services_basement": - return "com/google/android/gms/common/internal/Preconditions.class" + case 'androidx_core_core': + return 'androidx/core/util/Preconditions.class' + case 'com_google_guava_guava_android': + return 'com/google/common/base/Preconditions.class' + case 'google_play_services_basement': + return 'com/google/android/gms/common/internal/Preconditions.class' } return null } + private static void updateReadmeReferenceFile(List directories, String readmePath) { + File refFile = new File(readmePath) + refFile.write(JsonOutput.prettyPrint(JsonOutput.toJson(directories)) + '\n') + } + + private void updateBuildTargetDeclaration(ChromiumDepGraph depGraph, String normalisedRepoPath) { + File buildFile = new File("${normalisedRepoPath}/BUILD.gn") + StringBuilder sb = new StringBuilder() + + // Comparator to sort the dependency in alphabetical order, with the visible ones coming + // before all the internal ones. + Closure dependencyComparator = { dependency1, dependency2 -> + int visibilityResult = Boolean.compare(dependency1.visible, dependency2.visible) + if (visibilityResult != 0) { + return -visibilityResult + } + return dependency1.id <=> dependency2.id + } + + List fixedDependencies = depGraph.dependencies.values().findAll { + dependency -> dependency.usedInBuild + } + fixedDependencies.sort(dependencyComparator).each { dependency -> + appendBuildTarget(dependency, depGraph.dependencies, sb) + } + + sb.append('if (!limit_android_deps) {\n') + List buildWithChromiumDependencies + buildWithChromiumDependencies = depGraph.dependencies.values().findAll { + dependency -> !dependency.usedInBuild + } + buildWithChromiumDependencies.sort(dependencyComparator).each { dependency -> + appendBuildTarget(dependency, depGraph.dependencies, sb) + } + sb.append('}\n') + + String out = "${BUILD_GN_TOKEN_START}\n$sb\n${BUILD_GN_TOKEN_END}" + if (buildFile.exists()) { + Matcher matcher = BUILD_GN_GEN_PATTERN.matcher(buildFile.text) + if (!matcher.find()) { + throw new IllegalStateException('BUILD.gn insertion point not found.') + } + out = matcher.replaceFirst(out) + } else { + out = 'import("//build/config/android/rules.gni")\n' + out + } + buildFile.write(out) + } + + private String generateInternalTargetVisibilityLine() { + return "visibility = ${makeGnArray(internalTargetVisibility)}\n" + } + private void updateDepsDeclaration(ChromiumDepGraph depGraph, String cipdBucket, String repoPath, String depsFilePath) { File depsFile = new File(depsFilePath) - def sb = new StringBuilder() + StringBuilder sb = new StringBuilder() // Note: The string we're inserting is nested 1 level, hence the 2 leading spaces. Same // applies to the multiline package declaration string below. - sb.append(" # Generated by //third_party/android_deps/fetch_all.py") + sb.append(' # Generated by //third_party/android_deps/fetch_all.py') // Comparator to sort the dependencies in alphabetical order. - def dependencyComparator = { dependency1, dependency2 -> - return dependency1.id.compareTo(dependency2.id) + Closure dependencyComparator = { dependency1, dependency2 -> + dependency1.id <=> dependency2.id } depGraph.dependencies.values().sort(dependencyComparator).each { dependency -> - if (excludeDependency(dependency) || - computeJavaGroupForwardingTarget(dependency) != null) { + if (excludeDependency(dependency) || computeJavaGroupForwardingTarget(dependency)) { return } - def depPath = "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" - def cipdPath = "${cipdBucket}/${repoPath}/${depPath}" + String depPath = "${DOWNLOAD_DIRECTORY_NAME}/${dependency.directoryName}" + String cipdPath = "${cipdBucket}/${repoPath}/${depPath}" sb.append("""\ | | 'src/${repoPath}/${depPath}': { @@ -752,56 +991,15 @@ class BuildConfigGenerator extends DefaultTask { |""".stripMargin()) } - def matcher = DEPS_GEN_PATTERN.matcher(depsFile.getText()) - if (!matcher.find()) throw new IllegalStateException("DEPS insertion point not found.") - depsFile.write(matcher.replaceFirst("${DEPS_TOKEN_START}\n${sb}\n ${DEPS_TOKEN_END}")) - } - - private static void updateReadmeReferenceFile(List directories, String readmePath) { - File refFile = new File(readmePath) - refFile.write(JsonOutput.prettyPrint(JsonOutput.toJson(directories)) + "\n") - } - - public boolean excludeDependency(ChromiumDepGraph.DependencyDescription dependency) { - if (dependency.exclude || EXISTING_LIBS.get(dependency.id) != null) { - return true - } - boolean isAndroidxRepository = (repositoryPath == "third_party/androidx") - boolean isAndroidxDependency = (dependency.id.startsWith("androidx")) - if (isAndroidxRepository != isAndroidxDependency) { - return true; - } - if (repositoryPath == AUTOROLLED_REPO_PATH) { - def targetName = translateTargetName(dependency.id) + "_java" - return !isTargetAutorolled(targetName) - } - // TODO(crbug.com/1184780): Remove this once org_robolectric_shadows_multidex - // is updated to a newer version which does not need jetify. - if (dependency.directoryName == "org_robolectric_shadows_multidex") { - if (dependency.version != "4.3.1") { - throw new RuntimeException("Got a new version for org_robolectric_shadows_multidex. " + - "If this new version don't need jetify, please move this dependency back to the " + - "auto-generated section in //DEPS and //third_party/android_deps/BUILD.gn.") - } - return true - } - return false - } - - /** - * If |dependency| should be a java_group(), returns target to forward to. Returns null - * otherwise. - */ - public String computeJavaGroupForwardingTarget(ChromiumDepGraph.DependencyDescription dependency) { - def targetName = translateTargetName(dependency.id) + "_java" - if (repositoryPath != AUTOROLLED_REPO_PATH && isTargetAutorolled(targetName)) { - return "//${AUTOROLLED_REPO_PATH}:${targetName}" + Matcher matcher = DEPS_GEN_PATTERN.matcher(depsFile.text) + if (!matcher.find()) { + throw new IllegalStateException('DEPS insertion point not found.') } - return null - } + depsFile.write(matcher.replaceFirst("${DEPS_TOKEN_START}\n${sb}\n ${DEPS_TOKEN_END}")) + } - private boolean isTargetAutorolled(targetName) { - for (autorolledLibPrefix in AUTOROLLED_LIB_PREFIXES) { + private boolean isTargetAutorolled(String targetName) { + for (String autorolledLibPrefix in AUTOROLLED_LIB_PREFIXES) { if (targetName.startsWith(autorolledLibPrefix)) { return true } @@ -813,224 +1011,4 @@ class BuildConfigGenerator extends DefaultTask { return project.file("${chromiumSourceRoot}/${pathRelativeToChromiumRoot}").absolutePath } - private static String makeGnArray(String[] values) { - def sb = new StringBuilder(); - sb.append("["); - for (String value : values) { - sb.append("\""); - sb.append(value); - sb.append("\","); - } - sb.replace(sb.length() - 1, sb.length(), "]"); - return sb.toString(); - } - - static String makeOwners() { - // Make it easier to upgrade existing dependencies without full third_party review. - return "file://third_party/android_deps/OWNERS\n" - } - - static String makeReadme(ChromiumDepGraph.DependencyDescription dependency) { - def licenseStrings = [] - for (ChromiumDepGraph.LicenseSpec license : dependency.licenses) { - // Replace license names with ones that are whitelisted, see third_party/PRESUBMIT.py - switch (license.name) { - case "The Apache License, Version 2.0": - case "The Apache Software License, Version 2.0": - licenseStrings.add("Apache Version 2.0") - break - case "GNU General Public License, version 2, with the Classpath Exception": - licenseStrings.add("GPL v2 with the classpath exception") - break - default: - licenseStrings.add(license.name) - } - } - def licenseString = String.join(", ", licenseStrings) - - def securityCritical = dependency.supportsAndroid && dependency.isShipped - def licenseFile = dependency.isShipped? "LICENSE" : "NOT_SHIPPED" - - return """\ - Name: ${dependency.displayName} - Short Name: ${dependency.name} - URL: ${dependency.url} - Version: ${dependency.version} - License: ${licenseString} - License File: ${licenseFile} - Security Critical: ${securityCritical? "yes" : "no"} - ${dependency.licenseAndroidCompatible? "License Android Compatible: yes" : ""} - Description: - ${dependency.description} - - Local Modifications: - No modifications. - """.stripIndent() - } - - static String makeCipdYaml(ChromiumDepGraph.DependencyDescription dependency, String cipdBucket, - String repoPath) { - def cipdVersion = "${THREEPP_EPOCH}@${dependency.version}.${dependency.cipdSuffix}" - def cipdPath = "${cipdBucket}/${repoPath}" - // CIPD does not allow uppercase in names. - cipdPath += "/${DOWNLOAD_DIRECTORY_NAME}/" + dependency.directoryName - - // NOTE: the fetch_all.py script relies on the format of this file! - // See fetch_all.py:GetCipdPackageInfo(). - // NOTE: keep the copyright year 2018 until this generated code is - // updated, avoiding annual churn of all cipd.yaml files. - def str = """\ - # Copyright 2018 The Chromium Authors. All rights reserved. - # Use of this source code is governed by a BSD-style license that can be - # found in the LICENSE file. - - # To create CIPD package run the following command. - # cipd create --pkg-def cipd.yaml -tag version:${cipdVersion} - package: ${cipdPath} - description: "${dependency.displayName}" - data: - - file: ${dependency.fileName} - """.stripIndent() - - return str - } - - static void validateLicenses(ChromiumDepGraph.DependencyDescription dependency) { - if (dependency.licenses.isEmpty()) { - throw new RuntimeException("Missing license for ${dependency.id}.") - return - } - - for (ChromiumDepGraph.LicenseSpec license : dependency.licenses) { - if (!license.path?.trim() && !license.url?.trim()) { - def exceptionMessage = "Missing license for ${dependency.id}. " - + "License Name was: ${license.name}" - throw new RuntimeException(exceptionMessage) - } - } - } - - static void downloadLicenses(ChromiumDepGraph.DependencyDescription dependency, - String normalisedRepoPath, - ExecutorService downloadExecutor, - List downloadTasks) { - def depDir = normalisedRepoPath +"/" + computeDepDir(dependency) - for (int i = 0; i < dependency.licenses.size(); ++i) { - def license = dependency.licenses[i] - if (!license.path?.trim() && license.url?.trim()) { - def destFileSuffix = (dependency.licenses.size() > 1) ? "${i+1}.tmp" : "" - def destFile = new File("${depDir}/LICENSE${destFileSuffix}") - downloadTasks.add(downloadExecutor.submit { - downloadFile(dependency.id, license.url, destFile) - if (destFile.text.contains(" - try { - out << connectAndFollowRedirects(id, sourceUrl).getInputStream() - } catch (Exception e) { - throw new RuntimeException("Failed to fetch license for " + id + " url: " + sourceUrl, e) - } - } - } - } diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy index 5cd52954635d41..8470e453a9b566 100644 --- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy +++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy @@ -11,328 +11,323 @@ import org.gradle.api.artifacts.ResolvedConfiguration import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.artifacts.ResolvedModuleVersion import org.gradle.api.artifacts.component.ComponentIdentifier -import org.gradle.api.artifacts.result.ArtifactResult -import org.gradle.api.artifacts.result.ResolvedArtifactResult import org.gradle.api.logging.Logger /** - * Parses the project dependencies and generates a graph of - * {@link ChromiumDepGraph.DependencyDescription} objects to make the data manipulation easier. + * Parses the project dependencies and generates a graph of {@link ChromiumDepGraph.DependencyDescription} objects to + * make the data manipulation easier. */ class ChromiumDepGraph { - final def dependencies = new HashMap() - final def lowerVersionOverride = new HashSet() - - // Some libraries don't properly fill their POM with the appropriate licensing information. - // It is provided here from manual lookups. Note that licenseUrl must provide textual content - // rather than be an html page. - final def PROPERTY_OVERRIDES = [ - 'androidx_multidex_multidex': new PropertyOverride( + + // Some libraries don't properly fill their POM with the appropriate licensing information. It is provided here from + // manual lookups. Note that licenseUrl must provide textual content rather than be an html page. + static final Map PROPERTY_OVERRIDES = [ + androidx_multidex_multidex: new PropertyOverride( url: 'https://maven.google.com/androidx/multidex/multidex/2.0.0/multidex-2.0.0.aar'), - 'com_android_tools_desugar_jdk_libs': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/google/desugar_jdk_libs/master/LICENSE", - licenseName: "GNU General Public License, version 2, with the Classpath Exception", + com_android_tools_desugar_jdk_libs: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/google/desugar_jdk_libs/master/LICENSE', + licenseName: 'GNU General Public License, version 2, with the Classpath Exception', generateTarget: false), - 'com_android_tools_desugar_jdk_libs_configuration': new PropertyOverride( - licensePath: "licenses/desugar_jdk_libs_configuration.txt", - licenseName: "BSD 3-Clause", + com_android_tools_desugar_jdk_libs_configuration: new PropertyOverride( + licensePath: 'licenses/desugar_jdk_libs_configuration.txt', + licenseName: 'BSD 3-Clause', generateTarget: false), - 'backport_util_concurrent_backport_util_concurrent': new PropertyOverride( - licensePath: "licenses/CC01.0.txt", - licenseName: "CC0 1.0"), - 'classworlds_classworlds': new PropertyOverride( - description: "A class loader framework.", - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'com_github_kevinstern_software_and_algorithms': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/KevinStern/software-and-algorithms/master/LICENSE", - licenseName: "MIT License"), - 'com_google_android_datatransport_transport_api': new PropertyOverride( - description: "Interfaces for data logging in GmsCore SDKs."), - 'com_google_android_datatransport_transport_backend_cct': new PropertyOverride( + backport_util_concurrent_backport_util_concurrent: new PropertyOverride( + licensePath: 'licenses/CC01.0.txt', + licenseName: 'CC0 1.0'), + classworlds_classworlds: new PropertyOverride( + description: 'A class loader framework.', + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + com_github_kevinstern_software_and_algorithms: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/KevinStern/software-and-algorithms/master/LICENSE', + licenseName: 'MIT License'), + com_google_android_datatransport_transport_api: new PropertyOverride( + description: 'Interfaces for data logging in GmsCore SDKs.'), + com_google_android_datatransport_transport_backend_cct: new PropertyOverride( exclude: true), // We're not using datatransport functionality. - 'com_google_android_datatransport_transport_runtime': new PropertyOverride( + com_google_android_datatransport_transport_runtime: new PropertyOverride( exclude: true), // We're not using datatransport functionality. - 'com_google_android_gms_play_services_cloud_messaging': new PropertyOverride( - description: "Firebase Cloud Messaging library that interfaces with GmsCore."), - 'com_google_auto_auto_common': new PropertyOverride( - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_auto_service_auto_service': new PropertyOverride( - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_auto_service_auto_service_annotations': new PropertyOverride( - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_auto_value_auto_value_annotations': new PropertyOverride( - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_code_findbugs_jFormatString': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/spotbugs/spotbugs/master/spotbugs/licenses/LICENSE.txt", - licenseName: "GNU Lesser Public License"), - 'com_google_code_gson_gson': new PropertyOverride( - url: "https://github.com/google/gson", - licenseUrl: "https://raw.githubusercontent.com/google/gson/master/LICENSE", - licenseName: "Apache 2.0"), - 'com_google_errorprone_error_prone_annotation': new PropertyOverride( - url: "https://errorprone.info/", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_errorprone_error_prone_annotations': new PropertyOverride( - url: "https://errorprone.info/", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_firebase_firebase_annotations': new PropertyOverride( - description: "Common annotations for Firebase SKDs."), - 'com_google_firebase_firebase_common': new PropertyOverride( - description: "Common classes for Firebase SDKs."), - 'com_google_firebase_firebase_components': new PropertyOverride( - description: "Provides dependency management for Firebase SDKs."), - 'com_google_firebase_firebase_datatransport': new PropertyOverride( + com_google_android_gms_play_services_cloud_messaging: new PropertyOverride( + description: 'Firebase Cloud Messaging library that interfaces with GmsCore.'), + com_google_auto_auto_common: new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_auto_service_auto_service: new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_auto_service_auto_service_annotations: new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_auto_value_auto_value_annotations: new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_code_findbugs_jFormatString: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/spotbugs/spotbugs/master/spotbugs/licenses/LICENSE.txt', + licenseName: 'GNU Lesser Public License'), + com_google_code_gson_gson: new PropertyOverride( + url: 'https://github.com/google/gson', + licenseUrl: 'https://raw.githubusercontent.com/google/gson/master/LICENSE', + licenseName: 'Apache 2.0'), + com_google_errorprone_error_prone_annotation: new PropertyOverride( + url: 'https://errorprone.info/', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_errorprone_error_prone_annotations: new PropertyOverride( + url: 'https://errorprone.info/', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_firebase_firebase_annotations: new PropertyOverride( + description: 'Common annotations for Firebase SKDs.'), + com_google_firebase_firebase_common: new PropertyOverride( + description: 'Common classes for Firebase SDKs.'), + com_google_firebase_firebase_components: new PropertyOverride( + description: 'Provides dependency management for Firebase SDKs.'), + com_google_firebase_firebase_datatransport: new PropertyOverride( exclude: true), // We're not using datatransport functionality. - 'com_google_firebase_firebase_encoders_json': new PropertyOverride( - description: "JSON encoders used in Firebase SDKs."), - 'com_google_firebase_firebase_encoders': new PropertyOverride( - description: "Commonly used encoders for Firebase SKDs."), - 'com_google_firebase_firebase_iid_interop': new PropertyOverride( - description: "Interface library for Firebase IID SDK."), - 'com_google_firebase_firebase_iid': new PropertyOverride( - description: "Firebase IID SDK to get access to Instance IDs."), - 'com_google_firebase_firebase_installations_interop': new PropertyOverride( - description: "Interface library for Firebase Installations SDK."), - 'com_google_firebase_firebase_installations': new PropertyOverride( - description: "Firebase Installations SDK containing the client libraries to manage FIS."), - 'com_google_firebase_firebase_measurement_connector': new PropertyOverride( - description: "Bridge interfaces for Firebase analytics into GmsCore."), - 'com_google_firebase_firebase_messaging': new PropertyOverride( - description: "Firebase Cloud Messaging SDK to send and receive push messages via FCM."), - 'com_google_googlejavaformat_google_java_format': new PropertyOverride( - url: "https://github.com/google/google-java-format", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_guava_failureaccess': new PropertyOverride( - url: "https://github.com/google/guava", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_guava_guava': new PropertyOverride( - url: "https://github.com/google/guava", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_guava_guava_android': new PropertyOverride( - url: "https://github.com/google/guava", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_guava_listenablefuture': new PropertyOverride( - url: "https://github.com/google/guava", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'nekohtml_nekohtml': new PropertyOverride( - description: "NekoHTML is a simple HTML scanner and tag balancer."), - 'nekohtml_xercesMinimal': new PropertyOverride( - description: "Only contains necessary framework & Xerces2 classes", + com_google_firebase_firebase_encoders_json: new PropertyOverride( + description: 'JSON encoders used in Firebase SDKs.'), + com_google_firebase_firebase_encoders: new PropertyOverride( + description: 'Commonly used encoders for Firebase SKDs.'), + com_google_firebase_firebase_iid_interop: new PropertyOverride( + description: 'Interface library for Firebase IID SDK.'), + com_google_firebase_firebase_iid: new PropertyOverride( + description: 'Firebase IID SDK to get access to Instance IDs.'), + com_google_firebase_firebase_installations_interop: new PropertyOverride( + description: 'Interface library for Firebase Installations SDK.'), + com_google_firebase_firebase_installations: new PropertyOverride( + description: 'Firebase Installations SDK containing the client libraries to manage FIS.'), + com_google_firebase_firebase_measurement_connector: new PropertyOverride( + description: 'Bridge interfaces for Firebase analytics into GmsCore.'), + com_google_firebase_firebase_messaging: new PropertyOverride( + description: 'Firebase Cloud Messaging SDK to send and receive push messages via FCM.'), + com_google_googlejavaformat_google_java_format: new PropertyOverride( + url: 'https://github.com/google/google-java-format', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_guava_failureaccess: new PropertyOverride( + url: 'https://github.com/google/guava', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_guava_guava: new PropertyOverride( + url: 'https://github.com/google/guava', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_guava_guava_android: new PropertyOverride( + url: 'https://github.com/google/guava', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_guava_listenablefuture: new PropertyOverride( + url: 'https://github.com/google/guava', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + nekohtml_nekohtml: new PropertyOverride( + description: 'NekoHTML is a simple HTML scanner and tag balancer.'), + nekohtml_xercesMinimal: new PropertyOverride( + description: 'Only contains necessary framework & Xerces2 classes', url: 'http://nekohtml.sourceforge.net/index.html', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_ant_ant': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_ant_ant: new PropertyOverride( url: 'https://ant.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_ant_ant_launcher': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_ant_ant_launcher: new PropertyOverride( url: 'https://ant.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_ant_tasks': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_ant_tasks: new PropertyOverride( url: 'https://ant.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_artifact': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_artifact: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_artifact_manager': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_artifact_manager: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_error_diagnostics': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_error_diagnostics: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_model': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_model: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_plugin_registry': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_plugin_registry: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_profile': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_profile: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_project': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_project: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_repository_metadata': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_repository_metadata: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_maven_settings': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_maven_settings: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_wagon_wagon_file': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_wagon_wagon_file: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_wagon_wagon_http_lightweight': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_wagon_wagon_http_lightweight: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_wagon_wagon_http_shared': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_wagon_wagon_http_shared: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_apache_maven_wagon_wagon_provider_api': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_apache_maven_wagon_wagon_provider_api: new PropertyOverride( url: 'https://maven.apache.org/', - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_codehaus_mojo_animal_sniffer_annotations': new PropertyOverride( - url: "http://www.mojohaus.org/animal-sniffer/animal-sniffer-annotations/", - licenseUrl: "https://raw.githubusercontent.com/mojohaus/animal-sniffer/master/animal-sniffer-annotations/pom.xml", - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_codehaus_plexus_plexus_container_default': new PropertyOverride( - url: "https://codehaus-plexus.github.io/", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_codehaus_plexus_plexus_interpolation': new PropertyOverride( - url: "https://codehaus-plexus.github.io/", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'org_codehaus_plexus_plexus_utils': new PropertyOverride( - url: "https://codehaus-plexus.github.io/", - licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt", - licenseName: "Apache 2.0"), - 'com_google_protobuf_protobuf_java': new PropertyOverride( - url: "https://github.com/protocolbuffers/protobuf/blob/master/java/README.md", - licenseUrl: "https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE", - licenseName: "BSD"), - 'com_google_protobuf_protobuf_javalite': new PropertyOverride( - url: "https://github.com/protocolbuffers/protobuf/blob/master/java/lite.md", - licenseUrl: "https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE", - licenseName: "BSD"), - 'com_google_protobuf_protobuf_lite': new PropertyOverride( - url: "https://github.com/protocolbuffers/protobuf/blob/master/java/lite.md", - licenseUrl: "https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE", - licenseName: "BSD"), - 'com_google_ar_core': new PropertyOverride( - url: "https://github.com/google-ar/arcore-android-sdk", - licenseUrl: "https://raw.githubusercontent.com/google-ar/arcore-android-sdk/master/LICENSE", - licenseName: "Apache 2.0"), - 'commons_cli_commons_cli': new PropertyOverride( - licenseName: "Apache 2.0", - licenseUrl: "https://raw.githubusercontent.com/apache/commons-cli/master/LICENSE.txt"), - 'javax_annotation_javax_annotation_api': new PropertyOverride( + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_codehaus_mojo_animal_sniffer_annotations: new PropertyOverride( + url: 'http://www.mojohaus.org/animal-sniffer/animal-sniffer-annotations/', + /* groovylint-disable-next-line LineLength */ + licenseUrl: 'https://raw.githubusercontent.com/mojohaus/animal-sniffer/master/animal-sniffer-annotations/pom.xml', + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_codehaus_plexus_plexus_container_default: new PropertyOverride( + url: 'https://codehaus-plexus.github.io/', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_codehaus_plexus_plexus_interpolation: new PropertyOverride( + url: 'https://codehaus-plexus.github.io/', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + org_codehaus_plexus_plexus_utils: new PropertyOverride( + url: 'https://codehaus-plexus.github.io/', + licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt', + licenseName: 'Apache 2.0'), + com_google_protobuf_protobuf_java: new PropertyOverride( + url: 'https://github.com/protocolbuffers/protobuf/blob/master/java/README.md', + licenseUrl: 'https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE', + licenseName: 'BSD'), + com_google_protobuf_protobuf_javalite: new PropertyOverride( + url: 'https://github.com/protocolbuffers/protobuf/blob/master/java/lite.md', + licenseUrl: 'https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE', + licenseName: 'BSD'), + com_google_protobuf_protobuf_lite: new PropertyOverride( + url: 'https://github.com/protocolbuffers/protobuf/blob/master/java/lite.md', + licenseUrl: 'https://raw.githubusercontent.com/protocolbuffers/protobuf/master/LICENSE', + licenseName: 'BSD'), + com_google_ar_core: new PropertyOverride( + url: 'https://github.com/google-ar/arcore-android-sdk', + licenseUrl: 'https://raw.githubusercontent.com/google-ar/arcore-android-sdk/master/LICENSE', + licenseName: 'Apache 2.0'), + commons_cli_commons_cli: new PropertyOverride( + licenseName: 'Apache 2.0', + licenseUrl: 'https://raw.githubusercontent.com/apache/commons-cli/master/LICENSE.txt'), + javax_annotation_javax_annotation_api: new PropertyOverride( isShipped: false, // Annotations are stripped by R8. - licenseName: "CDDLv1.1", - licensePath: "licenses/CDDLv1.1.txt"), - 'javax_annotation_jsr250_api': new PropertyOverride( + licenseName: 'CDDLv1.1', + licensePath: 'licenses/CDDLv1.1.txt'), + javax_annotation_jsr250_api: new PropertyOverride( isShipped: false, // Annotations are stripped by R8. - licenseName: "CDDLv1.0", - licensePath: "licenses/CDDLv1.0.txt"), - 'net_sf_kxml_kxml2': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/stefanhaustein/kxml2/master/license.txt", - licenseName: "MIT"), - 'org_checkerframework_checker_compat_qual': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt", - licenseName: "GPL v2 with the classpath exception"), - 'org_checkerframework_checker_qual': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt", - licenseName: "GPL v2 with the classpath exception"), - 'org_checkerframework_dataflow': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt", - licenseName: "GPL v2 with the classpath exception"), - 'org_checkerframework_dataflow_shaded': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt", - licenseName: "GPL v2 with the classpath exception"), - 'org_checkerframework_javacutil': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt", - licenseName: "GPL v2 with the classpath exception"), - 'org_ow2_asm_asm': new PropertyOverride( - licenseUrl: "https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt", - licenseName: "BSD"), - 'org_ow2_asm_asm_analysis': new PropertyOverride( - licenseUrl: "https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt", - licenseName: "BSD"), - 'org_ow2_asm_asm_commons': new PropertyOverride( - licenseUrl: "https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt", - licenseName: "BSD"), - 'org_ow2_asm_asm_tree': new PropertyOverride( - licenseUrl: "https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt", - licenseName: "BSD"), - 'org_ow2_asm_asm_util': new PropertyOverride( - licenseUrl: "https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt", - licenseName: "BSD"), - 'org_pcollections_pcollections': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/hrldcpr/pcollections/master/LICENSE", - licenseName: "The MIT License"), - 'org_plumelib_plume_util': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/plume-lib/plume-util/master/LICENSE", - licenseName: "MIT"), - 'org_plumelib_require_javadoc': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/plume-lib/require-javadoc/master/LICENSE", - licenseName: "MIT"), - 'org_plumelib_reflection_util': new PropertyOverride( - licenseUrl: "https://raw.githubusercontent.com/plume-lib/reflection-util/master/LICENSE", - licenseName: "MIT"), - 'org_robolectric_annotations': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_junit': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_pluginapi': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_plugins_maven_dependency_resolver': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_resources': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_robolectric': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_sandbox': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_shadowapi': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_shadows_framework': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_shadows_multidex': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT", - cipdSuffix: "cr1"), - 'org_robolectric_shadows_playservices': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_utils': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), - 'org_robolectric_utils_reflector': new PropertyOverride( - licensePath: "licenses/Codehaus_License-2009.txt", - licenseName: "MIT"), + licenseName: 'CDDLv1.0', + licensePath: 'licenses/CDDLv1.0.txt'), + net_sf_kxml_kxml2: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/stefanhaustein/kxml2/master/license.txt', + licenseName: 'MIT'), + org_checkerframework_checker_compat_qual: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt', + licenseName: 'GPL v2 with the classpath exception'), + org_checkerframework_checker_qual: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt', + licenseName: 'GPL v2 with the classpath exception'), + org_checkerframework_dataflow: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt', + licenseName: 'GPL v2 with the classpath exception'), + org_checkerframework_dataflow_shaded: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt', + licenseName: 'GPL v2 with the classpath exception'), + org_checkerframework_javacutil: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/typetools/checker-framework/master/LICENSE.txt', + licenseName: 'GPL v2 with the classpath exception'), + org_ow2_asm_asm: new PropertyOverride( + licenseUrl: 'https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt', + licenseName: 'BSD'), + org_ow2_asm_asm_analysis: new PropertyOverride( + licenseUrl: 'https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt', + licenseName: 'BSD'), + org_ow2_asm_asm_commons: new PropertyOverride( + licenseUrl: 'https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt', + licenseName: 'BSD'), + org_ow2_asm_asm_tree: new PropertyOverride( + licenseUrl: 'https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt', + licenseName: 'BSD'), + org_ow2_asm_asm_util: new PropertyOverride( + licenseUrl: 'https://gitlab.ow2.org/asm/asm/raw/master/LICENSE.txt', + licenseName: 'BSD'), + org_pcollections_pcollections: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/hrldcpr/pcollections/master/LICENSE', + licenseName: 'The MIT License'), + org_plumelib_plume_util: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/plume-lib/plume-util/master/LICENSE', + licenseName: 'MIT'), + org_plumelib_require_javadoc: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/plume-lib/require-javadoc/master/LICENSE', + licenseName: 'MIT'), + org_plumelib_reflection_util: new PropertyOverride( + licenseUrl: 'https://raw.githubusercontent.com/plume-lib/reflection-util/master/LICENSE', + licenseName: 'MIT'), + org_robolectric_annotations: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_junit: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_pluginapi: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_plugins_maven_dependency_resolver: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_resources: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_robolectric: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_sandbox: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_shadowapi: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_shadows_framework: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_shadows_multidex: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT', + cipdSuffix: 'cr1'), + org_robolectric_shadows_playservices: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_utils: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), + org_robolectric_utils_reflector: new PropertyOverride( + licensePath: 'licenses/Codehaus_License-2009.txt', + licenseName: 'MIT'), ] - // Local text versions of HTML licenses. This cannot replace PROPERTY_OVERRIDES because some - // libraries refer to license templates such as https://opensource.org/licenses/MIT - // - // Keys should be 'https'. customizeLicenses() will normalize URLs to https. - final def LICENSE_OVERRIDES = [ + // Local text versions of HTML licenses. This cannot replace PROPERTY_OVERRIDES because some libraries refer to + // license templates such as https://opensource.org/licenses/MIT. + // Keys should be 'https', since customizeLicenses() will normalize URLs to https. + static final Map LICENSE_OVERRIDES = [ 'https://developer.android.com/studio/terms.html': 'licenses/Android_SDK_License-December_9_2016.txt', 'https://openjdk.java.net/legal/gplv2+ce.html': 'licenses/GNU_v2_with_Classpath_Exception_1991.txt', 'https://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web': 'licenses/SIL_Open_Font.txt', @@ -340,15 +335,42 @@ class ChromiumDepGraph { 'https://www.unicode.org/license.html': 'licenses/Unicode.txt', ] + final Map dependencies = [:] + final Set lowerVersionOverride = [] as Set + Project[] projects Logger logger boolean skipLicenses + static String makeModuleId(ResolvedModuleVersion module) { + // Does not include version because by default the resolution strategy for gradle is to use the newest version + // among the required ones. We want to be able to match it in the BUILD.gn file. + String moduleId = sanitize("${module.id.group}_${module.id.name}") + + // Add 'android' suffix for guava-android so that its module name is distinct from the module for guava. + if (module.id.name == 'guava' && 'android' in module.id.version) { + moduleId += '_android' + } + return moduleId + } + + static String makeModuleId(ResolvedArtifact artifact) { + // Does not include version because by default the resolution strategy for gradle is to use the newest version + // among the required ones. We want to be able to match it in the BUILD.gn file. + ComponentIdentifier componentId = artifact.id.componentIdentifier + String moduleId = sanitize("${componentId.group}_${componentId.module}") + + // Add 'android' suffix for guava-android so that its module name is distinct from the module for guava. + if (componentId.module == 'guava' && 'android' in componentId.version) { + moduleId += '_android' + } + return moduleId + } + void collectDependencies() { - Set deps = [] - Set buildDepsNoRecurse = [] - Set firstLevelModuleDependencies = [] - Map> resolvedArtifacts = new HashMap<>() + Set deps = [] as Set + Set buildDepsNoRecurse = [] as Set + Map> resolvedArtifacts = [:] String[] configNames = [ 'compile', 'compileListenableFuture', @@ -359,16 +381,16 @@ class ChromiumDepGraph { ] for (Project project : projects) { for (String configName : configNames) { - def config = project.configurations.getByName(configName).resolvedConfiguration + ResolvedConfiguration config = project.configurations.getByName(configName).resolvedConfiguration deps += config.firstLevelModuleDependencies if (configName == 'buildCompileNoDeps') { - buildDepsNoRecurse += config.firstLevelModuleDependencies + buildDepsNoRecurse += config.firstLevelModuleDependencies } if (!resolvedArtifacts.containsKey(configName)) { - resolvedArtifacts[configName] = [] + resolvedArtifacts[configName] = [] } resolvedArtifacts[configName].addAll(config.resolvedArtifacts) - } + } } resolvedArtifacts['compileListenableFuture'].each { artifact -> @@ -384,169 +406,136 @@ class ChromiumDepGraph { topLevelIds.each { id -> dependencies.get(id).visible = true } resolvedArtifacts['testCompile'].each { artifact -> - def id = makeModuleId(artifact) - def dep = dependencies.get(id) - assert dep != null : "No dependency collected for artifact ${artifact.name}" + String id = makeModuleId(artifact) + DependencyDescription dep = dependencies.get(id) + assert dep : "No dependency collected for artifact ${artifact.name}" dep.testOnly = true } resolvedArtifacts['androidTestCompile'].each { artifact -> - def dep = dependencies.get(makeModuleId(artifact)) - assert dep != null : "No dependency collected for artifact ${artifact.name}" + DependencyDescription dep = dependencies.get(makeModuleId(artifact)) + assert dep : "No dependency collected for artifact ${artifact.name}" dep.supportsAndroid = true dep.testOnly = true } resolvedArtifacts['buildCompile'].each { artifact -> - def id = makeModuleId(artifact) - def dep = dependencies.get(id) - assert dep != null : "No dependency collected for artifact ${artifact.name}" + String id = makeModuleId(artifact) + DependencyDescription dep = dependencies.get(id) + assert dep : "No dependency collected for artifact ${artifact.name}" dep.usedInBuild = true dep.testOnly = false } - def compileResolvedArtifacts = resolvedArtifacts['compile'] + List compileResolvedArtifacts = resolvedArtifacts['compile'] compileResolvedArtifacts += resolvedArtifacts['compileListenableFuture'] compileResolvedArtifacts.each { artifact -> - def id = makeModuleId(artifact) - def dep = dependencies.get(id) - assert dep != null : "No dependency collected for artifact ${artifact.name}" + String id = makeModuleId(artifact) + DependencyDescription dep = dependencies.get(id) + assert dep : "No dependency collected for artifact ${artifact.name}" dep.supportsAndroid = true dep.testOnly = false dep.isShipped = true } buildDepsNoRecurse.each { resolvedDep -> - def id = makeModuleId(resolvedDep.module) - def dep = dependencies.get(id) - assert dep != null : "No dependency collected for artifact ${artifact.name}" + String id = makeModuleId(resolvedDep.module) + DependencyDescription dep = dependencies.get(id) + assert dep : "No dependency collected for artifact ${artifact.name}" dep.usedInBuild = true dep.testOnly = false } - PROPERTY_OVERRIDES.each { id, fallbackProperties -> - if (fallbackProperties?.isShipped != null) { - def dep = dependencies.get(id) - if (dep != null) { + if (fallbackProperties?.isShipped) { + DependencyDescription dep = dependencies.get(id) + if (dep) { dep.isShipped = fallbackProperties.isShipped } else { - logger.warn("PROPERTY_OVERRIDES has stale dep: " + id) + logger.warn('PROPERTY_OVERRIDES has stale dep: ' + id) } } } } + private static String sanitize(String input) { + return input.replaceAll('[:.-]', '_') + } + private void collectDependenciesInternal(ResolvedDependency dependency, boolean recurse = true) { - def id = makeModuleId(dependency.module) + String id = makeModuleId(dependency.module) if (dependencies.containsKey(id)) { - if (dependencies.get(id).version == dependency.module.id.version) return + if (dependencies.get(id).version == dependency.module.id.version) { + return + } - // Default to using largest version for version conflict resolution. See - // crbug.com/1040958 + // Default to using largest version for version conflict resolution. See http://crbug.com/1040958. // https://docs.gradle.org/current/userguide/dependency_resolution.html#sec:version-conflict - def useLowerVersion = (id in lowerVersionOverride) - def versionIsLower = dependency.module.id.version < dependencies.get(id).version + boolean useLowerVersion = (id in lowerVersionOverride) + boolean versionIsLower = dependency.module.id.version < dependencies.get(id).version if (useLowerVersion != versionIsLower) { return } } - if (dependency.getModuleArtifacts().size() != 1) { + if (dependency.moduleArtifacts.size() != 1) { throw new IllegalStateException("The dependency ${id} does not have exactly one " + - "artifact: ${dependency.getModuleArtifacts()}") + "artifact: ${dependency.moduleArtifacts}") } - def artifact = dependency.getModuleArtifacts()[0] + ResolvedArtifact artifact = dependency.moduleArtifacts[0] if (artifact.extension != 'jar' && artifact.extension != 'aar') { throw new IllegalStateException("Type ${artifact.extension} of ${id} not supported.") } if (recurse) { - def childDependenciesWithArtifacts = [] + List childDependenciesWithArtifacts = [] - dependency.children.each { - childDependency-> - // Replace dependency which acts as a redirect - // (ex: org.jetbrains.kotlinx:kotlinx-coroutines-core) with + dependency.children.each { childDependency -> + // Replace dependency which acts as a redirect (ex: org.jetbrains.kotlinx:kotlinx-coroutines-core) with // dependencies it redirects to. - if (childDependency.getModuleArtifacts().isEmpty()) { - if (!childDependency.children.isEmpty()) { - childDependenciesWithArtifacts += childDependency.children - } else { - throw new IllegalStateException( - "The dependency ${id} has no children and no artifacts.") - } + if (childDependency.moduleArtifacts) { + childDependenciesWithArtifacts += childDependency } else { - childDependenciesWithArtifacts += childDependency + if (childDependency.children) { + childDependenciesWithArtifacts += childDependency.children + } else { + throw new IllegalStateException("The dependency ${id} has no children and no artifacts.") + } } - } - - def childModules = [] - childDependenciesWithArtifacts.each { childDependency -> - childModules += makeModuleId(childDependency.module) - } - dependencies.put(id, buildDepDescription(id, dependency, artifact, childModules)) - childDependenciesWithArtifacts.each { - childDependency -> collectDependenciesInternal(childDependency) - } - } else { - dependencies.put(id, buildDepDescription(id, dependency, artifact, [])) - } - } - - static String makeModuleId(ResolvedModuleVersion module) { - // Does not include version because by default the resolution strategy for gradle is to use - // the newest version among the required ones. We want to be able to match it in the - // BUILD.gn file. - def moduleId = sanitize("${module.id.group}_${module.id.name}") - - // Add 'android' suffix for guava-android so that its module name is distinct from the - // module for guava. - if (module.id.name == "guava" && module.id.version.contains("android")) { - moduleId += "_android" - } - return moduleId - } + } - static String makeModuleId(ResolvedArtifact artifact) { - // Does not include version because by default the resolution strategy for gradle is to use - // the newest version among the required ones. We want to be able to match it in the - // BUILD.gn file. - def componentId = artifact.id.componentIdentifier - def moduleId = sanitize("${componentId.group}_${componentId.module}") - - // Add 'android' suffix for guava-android so that its module name is distinct from the - // module for guava. - if (componentId.module == "guava" && componentId.version.contains("android")) { - moduleId += "_android" + List childModules = [] + childDependenciesWithArtifacts.each { childDependency -> + childModules += makeModuleId(childDependency.module) + } + dependencies.put(id, buildDepDescription(id, dependency, artifact, childModules)) + childDependenciesWithArtifacts.each { + childDependency -> collectDependenciesInternal(childDependency) + } + } else { + dependencies.put(id, buildDepDescription(id, dependency, artifact, [])) } - return moduleId } - private static String sanitize(String input) { - return input.replaceAll("[:.-]", "_") - } - - private buildDepDescription(String id, ResolvedDependency dependency, ResolvedArtifact artifact, - List childModules) { - def pomUrl, pomContent + private DependencyDescription buildDepDescription( + String id, ResolvedDependency dependency, ResolvedArtifact artifact, List childModules) { + String pomUrl + GPathResult pomContent (pomUrl, pomContent) = computePomFromArtifact(artifact) - def licenses = [] + List licenses = [] if (!skipLicenses) { - licenses = resolveLicenseInformation(id, pomContent) + licenses = resolveLicenseInformation(pomContent) } // Build |fileUrl| by swapping '.pom' file extension with artifact file extension. - def fileUrl = pomUrl.substring(0, pomUrl.length() - 3) + artifact.extension + String fileUrl = pomUrl[0..-4] + artifact.extension checkDownloadable(fileUrl) // Get rid of irrelevant indent that might be present in the XML file. - def description = pomContent.description?.text()?.trim()?.replaceAll(/\s+/, " ") - def displayName = pomContent.name?.text() - if (!displayName) { - displayName = dependency.module.id.name - } + String description = pomContent.description?.text()?.trim()?.replaceAll(/\s+/, ' ') + String displayName = pomContent.name?.text() + displayName = displayName ?: dependency.module.id.name return customizeDep(new DependencyDescription( id: id, @@ -565,82 +554,67 @@ class ChromiumDepGraph { url: pomContent.url?.text(), displayName: displayName, exclude: false, - cipdSuffix: "cr0", + cipdSuffix: 'cr0', )) - } + } - private void customizeLicenses(DependencyDescription dep, - PropertyOverride fallbackProperties) { + private void customizeLicenses(DependencyDescription dep, PropertyOverride fallbackProperties) { for (LicenseSpec license : dep.licenses) { if (!license.url) { continue } - def normalizedLicenseUrl = license.url.replace('http://', 'https://') - def licenseOverridePath = LICENSE_OVERRIDES[normalizedLicenseUrl] + String normalizedLicenseUrl = license.url.replace('http://', 'https://') + String licenseOverridePath = LICENSE_OVERRIDES[normalizedLicenseUrl] if (licenseOverridePath) { license.url = '' license.path = licenseOverridePath } } - if (dep.id?.startsWith("com_google_android_")) { - logger.debug("Using Android license for ${dep.id}") + if (dep.id?.startsWith('com_google_android_')) { + logger.debug("Using Android license for $dep.id") dep.licenses.clear() dep.licenses.add(new LicenseSpec( - name: "Android Software Development Kit License", - path: "licenses/Android_SDK_License-December_9_2016.txt")) + name: 'Android Software Development Kit License', + path: 'licenses/Android_SDK_License-December_9_2016.txt')) } - if (fallbackProperties != null) { - if (fallbackProperties.licenseName == null) { - if (fallbackProperties.licensePath != null - || fallbackProperties.licenseUrl != null) { - def errorMsg = "PropertyOverride must specify 'licenseName' if either " - + "'licensePath' or licenseUrl' is specified." - throw new IllegalStateException(errorMsg) + if (fallbackProperties) { + if (fallbackProperties.licenseName) { + dep.licenses.clear() + LicenseSpec license = new LicenseSpec( + name : fallbackProperties.licenseName, + path: fallbackProperties.licensePath, + url: fallbackProperties.licenseUrl, + ) + dep.licenses.add(license) + } else { + if (fallbackProperties.licensePath || fallbackProperties.licenseUrl) { + throw new IllegalStateException('PropertyOverride must specify "licenseName" if either ' + + '"licensePath" or "licenseUrl" is specified.') } - return } - - dep.licenses.clear() - def license = new LicenseSpec( - name : fallbackProperties.licenseName, - path: fallbackProperties.licensePath, - url: fallbackProperties.licenseUrl, - ) - dep.licenses.add(license) } } - private customizeDep(DependencyDescription dep) { - if (dep.id?.startsWith("com_google_android_")) { - if (!dep.url) { - dep.url = "https://developers.google.com/android/guides/setup" - } - } else if (dep.id?.startsWith("com_google_firebase_")) { - // Some firebase dependencies don't set their URL. - if (!dep.url) { - dep.url = "https://firebase.google.com" - } + private DependencyDescription customizeDep(DependencyDescription dep) { + if (dep.id?.startsWith('com_google_android_')) { + // Many google dependencies don't set their URL, here is a good default. + dep.url = dep.url ?: 'https://developers.google.com/android/guides/setup' + } else if (dep.id?.startsWith('com_google_firebase_')) { + // Same as above for some firebase dependencies. + dep.url = dep.url ?: 'https://firebase.google.com' } - def fallbackProperties = PROPERTY_OVERRIDES.get(dep.id) - if (fallbackProperties != null) { - logger.debug("Using fallback properties for ${dep.id}") - if (fallbackProperties.description != null) { - dep.description = fallbackProperties.description - } - if (fallbackProperties.url != null) { - dep.url = fallbackProperties.url - } - if (fallbackProperties.cipdSuffix != null) { - dep.cipdSuffix = fallbackProperties.cipdSuffix - } - if (fallbackProperties.generateTarget != null) { - dep.generateTarget = fallbackProperties.generateTarget - } - if (fallbackProperties.exclude != null) { - dep.exclude = fallbackProperties.exclude + PropertyOverride fallbackProperties = PROPERTY_OVERRIDES.get(dep.id) + if (fallbackProperties) { + logger.debug("Using fallback properties for $dep.id") + dep.with { + description = fallbackProperties.description ?: description + url = fallbackProperties.url ?: url + cipdSuffix = fallbackProperties.cipdSuffix ?: cipdSuffix + generateTarget = fallbackProperties.generateTarget ?: generateTarget + exclude = fallbackProperties.exclude ?: exclude } } @@ -656,56 +630,55 @@ class ChromiumDepGraph { return dep } - private resolveLicenseInformation(String id, GPathResult pomContent) { - GPathResult licenses = pomContent?.licenses?.license - if (!licenses) { - return [] - } + private List resolveLicenseInformation(GPathResult pomContent) { + GPathResult licenses = pomContent?.licenses?.license + if (!licenses) { + return [] + } - def out = [] - for (GPathResult license : licenses) { - out.add(new LicenseSpec( + List out = [] + for (GPathResult license : licenses) { + out.add(new LicenseSpec( name: license.name.text(), url: license.url.text() )) - } - return out + } + return out } - private computePomFromArtifact(ResolvedArtifact artifact) { + private List computePomFromArtifact(ResolvedArtifact artifact) { for (Project project : projects) { for (ArtifactRepository repository : project.repositories.asList()) { - def repoUrl = repository.properties.get('url').toString() - // Some repo url may have trailing '/' and this breaks the file - // url generation below. So remove it if present. + String repoUrl = repository.properties.get('url') + // Some repo url may have trailing '/' and this breaks the file url generation below. So remove it if + // present. if (repoUrl.endsWith('/')) { - repoUrl = repoUrl.substring(0, repoUrl.length() - 1) + repoUrl = repoUrl[0..-2] } - def component = artifact.id.componentIdentifier + ComponentIdentifier component = artifact.id.componentIdentifier // Constructs the file url for pom. For example, with // * repoUrl as "https://maven.google.com" // * component.group as "android.arch.core" // * component.module as "common" // * component.version as "1.1.1" // - // The file url will be: - // https://maven.google.com/android/arch/core/common/1.1.1/common-1.1.1.pom - def fileUrl = String.format("%s/%s/%s/%s/%s-%s.pom", + // The file url will be: https://maven.google.com/android/arch/core/common/1.1.1/common-1.1.1.pom + String fileUrl = String.format('%s/%s/%s/%s/%s-%s.pom', repoUrl, component.group.replace('.', '/'), component.module, component.version, component.module, - // While maven central and maven.google.com use "version", - // https://androidx.dev uses "timestampedVersion" as part - // of the file url - component.hasProperty("timestampedVersion") ? component.timestampedVersion : component.version) + // While maven central and maven.google.com use "version", https://androidx.dev uses + // "timestampedVersion" as part of the file url + component.hasProperty('timestampedVersion') ? component.timestampedVersion : component.version) try { - def content = new XmlSlurper(false /* validating */, false /* namespaceAware */).parse(fileUrl) - logger.debug("Succeeded in resolving url ${fileUrl}") + GPathResult content = new XmlSlurper( + false /* validating */, false /* namespaceAware */).parse(fileUrl) + logger.debug("Succeeded in resolving url $fileUrl") return [fileUrl, content] - } catch (Exception ignored) { - logger.debug("Failed in resolving url ${fileUrl}") + } catch (any) { + logger.debug("Failed in resolving url $fileUrl") } } } @@ -714,27 +687,28 @@ class ChromiumDepGraph { private void checkDownloadable(String url) { try { - def inStream = new URL(url).openStream() - if (inStream != null) { - inStream.close(); - logger.debug("Succeeded in resolving url ${url}") + InputStream inStream = new URL(url).openStream() + if (inStream) { + inStream.close() + logger.debug("Succeeded in resolving url $url") return } - } catch (Exception ignored) {} - throw new RuntimeException("Resolved POM but could not resolve ${url}") + } catch (any) { + throw new RuntimeException("Resolved POM but could not resolve $url") + } } @AutoClone static class DependencyDescription { + String id ResolvedArtifact artifact String group, name, version, extension, displayName, description, url List licenses String fileName, fileUrl - // The local directory name to store the files like artifact, license - // file, 3pp subdirectory, and etc. Must be lowercase since 3pp uses - // the directory name as part of the CIPD names. However CIPD does not - // allow uppercase in names. + // The local directory name to store the files like artifact, license file, 3pp subdirectory, and etc. Must be + // lowercase since 3pp uses the directory name as part of the CIPD names. However CIPD does not allow uppercase + // in names. String directoryName boolean supportsAndroid, visible, exclude, testOnly, isShipped, usedInBuild boolean generateTarget = true @@ -742,21 +716,27 @@ class ChromiumDepGraph { ComponentIdentifier componentId List children String cipdSuffix + } static class LicenseSpec { - String name, url, path + + String name, url, path + } static class PropertyOverride { - String description - String url - String licenseName, licenseUrl, licensePath - String cipdSuffix - Boolean isShipped - // Set to true if this dependency is not needed. - Boolean exclude - // Set to false to skip creation of BUILD.gn target. - Boolean generateTarget + + String description + String url + String licenseName, licenseUrl, licensePath + String cipdSuffix + Boolean isShipped + // Set to true if this dependency is not needed. + Boolean exclude + // Set to false to skip creation of BUILD.gn target. + Boolean generateTarget + } + } diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy index 1204e7a4bf2b41..7ff1e7ea244b9e 100644 --- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy +++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy @@ -7,20 +7,21 @@ import org.gradle.api.Project import org.gradle.api.artifacts.DependencyResolveDetails /** - * Plugin designed to define the configuration names to be used in the Gradle files to describe - * the dependencies that {@link ChromiumDepGraph} with pick up. + * Plugin designed to define the configuration names to be used in the Gradle files to describe the dependencies that + * {@link ChromiumDepGraph} with pick up. */ class ChromiumPlugin implements Plugin { + void apply(Project project) { - // The configurations here are going to be used in ChromiumDepGraph. Keep it up to date - // with the declarations below. + // The configurations here are going to be used in ChromiumDepGraph. Keep it up to date with the declarations + // below. project.configurations { /** Main type of configuration, use it for libraries that the APK depends on. */ compile /** - * Dedicated com_google_guava_listenablefuture configuration so that other libraries do - * not affect the resolved listenablefuture version. + * Dedicated com_google_guava_listenablefuture configuration so that other libraries do not affect the + * resolved listenablefuture version. */ compileListenableFuture @@ -30,27 +31,24 @@ class ChromiumPlugin implements Plugin { /** Libraries that are only used during build. These support android. */ buildCompile - /** - * Libraries that are only used during build but should not - * automatically retrieve their dependencies. - */ + /** Libraries that are only used during build but should not automatically retrieve their dependencies. */ buildCompileNoDeps /** Libraries that are used for testing only and support android. */ androidTestCompile } - project.configurations.all { - resolutionStrategy.eachDependency { DependencyResolveDetails details -> - if (project.ext.hasProperty("versionOverrideMap") && - project.ext.versionOverrideMap != null) { - def module = details.requested.group + ":" + details.requested.name - def version = project.ext.versionOverrideMap[module] - if (version != null) { - details.useVersion version - } - } + project.configurations.all { + resolutionStrategy.eachDependency { DependencyResolveDetails details -> + if (project.ext.hasProperty('versionOverrideMap') && project.ext.versionOverrideMap) { + module = "${details.requested.group}:${details.requested.name}" + version = project.ext.versionOverrideMap[module] + if (version != null) { + details.useVersion version + } + } + } } - } - } + } + }