| // 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. |
| |
| import groovy.json.JsonOutput |
| import org.gradle.api.DefaultTask |
| import org.gradle.api.tasks.TaskAction |
| |
| import java.util.regex.Pattern |
| import java.util.concurrent.Executors |
| import java.util.concurrent.TimeUnit |
| |
| /** |
| * 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: |
| * <pre> |
| * task myTaskName(type: BuildConfigGenerator) { |
| * repositoryPath 'build_files_and_repository_location/' |
| * } |
| * </pre> |
| */ |
| 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 BUILD_GN_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" |
| |
| // 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': |
| '//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', |
| ] |
| |
| /** |
| * Directory where the artifacts will be downloaded and where files will be generated. |
| * Note: this path is specified as relative to the chromium source root, and must be normalised |
| * to an absolute path before being used, as Groovy would base relative path where the script |
| * is being executed. |
| */ |
| String repositoryPath |
| |
| /** |
| * Relative path to the Chromium source root from the build.gradle file. |
| */ |
| String chromiumSourceRoot |
| |
| /** |
| * Name of the cipd root package. |
| */ |
| String cipdBucket |
| |
| /** |
| * Prefix of path to strip before uploading to CIPD. |
| */ |
| String stripFromCipdPath |
| |
| /** |
| * Skips license file import. |
| */ |
| boolean skipLicenses |
| |
| /** |
| * Only pull play services targets into BUILD.gn file. |
| * If the play services target depends on a non-play services target, it will use the target in |
| * //third_party/android_deps/BUILD.gn. |
| */ |
| boolean onlyPlayServices |
| |
| /** |
| * Array with visibility for targets which are not listed in build.gradle |
| */ |
| String[] internalTargetVisibility |
| |
| /** |
| * Whether to use dedicated directory for androidx dependencies. |
| */ |
| boolean useDedicatedAndroidxDir |
| |
| @TaskAction |
| void main() { |
| skipLicenses = skipLicenses || project.hasProperty("skipLicenses") |
| useDedicatedAndroidxDir |= project.hasProperty("useDedicatedAndroidxDir") |
| |
| def graph = new ChromiumDepGraph(project: project, skipLicenses: skipLicenses) |
| def 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 = [] |
| graph.dependencies.values().each { dependency -> |
| if (excludeDependency(dependency)) { |
| return |
| } |
| logger.debug "Processing ${dependency.name}: \n${jsonDump(dependency)}" |
| def depDir = "${DOWNLOAD_DIRECTORY_NAME}/${dependency.id}" |
| def absoluteDepDir = "${normalisedRepoPath}/${depDir}" |
| |
| dependencyDirectories.add(depDir) |
| |
| if (new File("${absoluteDepDir}/${dependency.fileName}").exists()) { |
| getLogger().quiet("${dependency.id} exists, skipping.") |
| return |
| } |
| |
| project.copy { |
| from dependency.artifact.file |
| into absoluteDepDir |
| } |
| |
| new File("${absoluteDepDir}/README.chromium").write(makeReadme(dependency)) |
| new File("${absoluteDepDir}/cipd.yaml").write(makeCipdYaml(dependency, cipdBucket, |
| stripFromCipdPath, |
| repositoryPath)) |
| new File("${absoluteDepDir}/OWNERS").write(makeOwners()) |
| if (!skipLicenses) { |
| if (!dependency.licensePath?.trim()?.isEmpty()) { |
| new File("${absoluteDepDir}/LICENSE").write( |
| new File("${normalisedRepoPath}/${dependency.licensePath}").text) |
| } else if (!dependency.licenseUrl?.trim()?.isEmpty()) { |
| File destFile = new File("${absoluteDepDir}/LICENSE") |
| downloadTasks.add(downloadExecutor.submit { |
| downloadFile(dependency.id, dependency.licenseUrl, destFile) |
| if (destFile.text.contains("<html")) { |
| throw new RuntimeException("Found HTML in LICENSE file. Please add an " |
| + "override to ChromiumDepGraph.groovy for ${dependency.id}.") |
| } |
| }) |
| } else { |
| getLogger().warn("Missing license for ${dependency.id}.") |
| getLogger().warn("License Name was: ${dependency.licenseName}") |
| } |
| } |
| } |
| downloadExecutor.shutdown() |
| // Check for exceptions. |
| for (def task : downloadTasks) { |
| task.get() |
| } |
| |
| // 3. Generate the root level build files |
| updateBuildTargetDeclaration(graph, repositoryPath, normalisedRepoPath) |
| updateDepsDeclaration(graph, cipdBucket, stripFromCipdPath, repositoryPath, |
| "${normalisedRepoPath}/../../DEPS") |
| dependencyDirectories.sort { path1, path2 -> return path1.compareTo(path2) } |
| updateReadmeReferenceFile(dependencyDirectories, |
| "${normalisedRepoPath}/additional_readme_paths.json") |
| } |
| |
| private void updateBuildTargetDeclaration(ChromiumDepGraph depGraph, |
| String repositoryPath, 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) |
| } |
| |
| depGraph.dependencies.values().sort(dependencyComparator).each { dependency -> |
| if (excludeDependency(dependency) || !dependency.generateTarget) { |
| return |
| } |
| |
| def targetName = translateTargetName(dependency.id) + "_java" |
| if (useDedicatedAndroidxDir && targetName.startsWith("androidx_")) { |
| assert dependency.extension == 'jar' || dependency.extension == 'aar' |
| sb.append(""" |
| java_group("${targetName}") { |
| deps = [ \"//third_party/androidx:${targetName}\" ] |
| """.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 = depGraph.dependencies[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)) { |
| depsStr += "\"//third_party/android_deps:${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.id}" |
| sb.append(BUILD_GN_GEN_REMINDER) |
| if (dependency.extension == 'jar') { |
| sb.append("""\ |
| java_prebuilt("${targetName}") { |
| jar_path = "${libPath}/${dependency.fileName}" |
| output_name = "${dependency.id}" |
| """.stripIndent()) |
| if (dependency.supportsAndroid) { |
| 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") |
| } |
| } else if (dependency.extension == 'aar') { |
| sb.append("""\ |
| 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") |
| } |
| |
| 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") |
| } |
| |
| 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) |
| } |
| buildFile.write(out) |
| } |
| |
| public static String translateTargetName(String targetName) { |
| if (isPlayServicesTarget(targetName)) { |
| return targetName.replaceFirst("com_", "").replaceFirst("android_gms_", "") |
| } |
| return targetName |
| } |
| |
| 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) |
| } |
| |
| public String generateBuildTargetVisibilityDeclaration( |
| ChromiumDepGraph.DependencyDescription dependency) { |
| def 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') |
| sb.append(' # be directly depended on. Please use :material_design_java instead.\n') |
| sb.append(generateInternalTargetVisibilityLine()) |
| return sb.toString() |
| case 'com_google_protobuf_protobuf_javalite': |
| sb.append(' # Protobuf runtime is pulled in via Doubledown, thus this target should not\n') |
| sb.append(' # be directly depended on. Please use :protobuf_lite_runtime_java instead.\n') |
| sb.append(generateInternalTargetVisibilityLine()) |
| return sb.toString() |
| } |
| |
| if (!dependency.visible) { |
| sb.append(' # To remove visibility constraint, add this dependency to\n') |
| sb.append(" # //${repositoryPath}/build.gradle.\n") |
| sb.append(generateInternalTargetVisibilityLine()) |
| } |
| return sb.toString() |
| } |
| |
| private String generateInternalTargetVisibilityLine() { |
| return 'visibility = ' + makeGnArray(internalTargetVisibility) + '\n' |
| } |
| |
| private static void addSpecialTreatment(StringBuilder sb, String dependencyId, String dependencyExtension) { |
| if (isPlayServicesTarget(dependencyId)) { |
| if (Pattern.matches(".*cast_framework.*", dependencyId)) { |
| sb.append(' # Removing all resources from cast framework as they are unused bloat.\n') |
| sb.append(' # Can only safely remove them when R8 will strip the path that accesses them.\n') |
| sb.append(' strip_resources = !is_java_debug\n') |
| } else { |
| sb.append(' # Removing drawables from GMS .aars as they are unused bloat.\n') |
| sb.append(' strip_drawables = true\n') |
| } |
| } |
| if (dependencyId.startsWith('org_robolectric')) { |
| // Skip platform checks since it depends on |
| // 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) { |
| case 'androidx_annotation_annotation': |
| sb.append(' # https://crbug.com/989505\n') |
| sb.append(' jar_excluded_patterns = ["META-INF/proguard/*"]\n') |
| 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') |
| break |
| case 'androidx_fragment_fragment': |
| sb.append("""\ |
| | deps += [ |
| | "//third_party/android_deps/utils:java", |
| | "//third_party/android_deps/local_modifications/androidx_fragment_fragment:androidx_fragment_fragment_prebuilt_java", |
| | ] |
| | # Omit this file since we use our own copy, included above. |
| | # We can remove this once we migrate to AndroidX master for all libraries. |
| | jar_excluded_patterns = [ |
| | "androidx/fragment/app/DialogFragment*", |
| | ] |
| | |
| | ignore_proguard_configs = true |
| | |
| | bytecode_rewriter_target = "//build/android/bytecode:fragment_activity_replacer" |
| |""".stripMargin()) |
| break |
| case 'androidx_media_media': |
| case 'androidx_versionedparcelable_versionedparcelable': |
| case 'com_android_support_support_media_compat': |
| 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') |
| break |
| case 'androidx_test_uiautomator_uiautomator': |
| sb.append(' deps = [":androidx_test_runner_java"]\n') |
| break |
| case 'androidx_mediarouter_mediarouter': |
| sb.append(' # https://crbug.com/1000382\n') |
| sb.append(' proguard_configs = ["androidx_mediarouter.flags"]\n') |
| break |
| case 'androidx_transition_transition': |
| // Not specified in the POM, compileOnly dependency not supposed to be used unless |
| // the library is present: b/70887421 |
| sb.append(' deps += [":androidx_fragment_fragment_java"]\n') |
| break |
| case 'androidx_vectordrawable_vectordrawable': |
| case 'com_android_support_support_vector_drawable': |
| // Target has AIDL, but we don't support it yet: http://crbug.com/644439 |
| sb.append(' create_srcjar = false\n') |
| break |
| case 'android_arch_lifecycle_runtime': |
| case 'android_arch_lifecycle_viewmodel': |
| case 'androidx_lifecycle_lifecycle_runtime': |
| case 'androidx_lifecycle_lifecycle_viewmodel': |
| sb.append('\n') |
| sb.append(' # https://crbug.com/887942#c1\n') |
| sb.append(' ignore_proguard_configs = true\n') |
| break |
| case 'com_android_support_coordinatorlayout': |
| case 'androidx_coordinatorlayout_coordinatorlayout': |
| sb.append('\n') |
| sb.append(' # Reduce binary size. https:crbug.com/954584\n') |
| sb.append(' ignore_proguard_configs = true\n') |
| break |
| case 'com_google_android_material_material': |
| sb.append('\n') |
| sb.append(' # Reduce binary size. https:crbug.com/954584\n') |
| sb.append(' ignore_proguard_configs = true\n') |
| break |
| case 'com_android_support_support_annotations': |
| sb.append(' # https://crbug.com/989505\n') |
| sb.append(' jar_excluded_patterns = ["META-INF/proguard/*"]\n') |
| break |
| case 'com_android_support_support_compat': |
| 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(' ignore_manifest = true\n') |
| // 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("""\ |
| | jar_excluded_patterns = [ |
| | "android/support/v4/graphics/drawable/IconCompatParcelizer.class", |
| | "android/support/v4/os/ResultReceiver*", |
| | "androidx/core/graphics/drawable/IconCompatParcelizer.class", |
| | "androidx/core/internal/package-info.class", |
| | "android/support/v4/app/INotificationSideChannel*", |
| | "android/support/v4/os/IResultReceiver*", |
| | ] |
| | |
| |""".stripMargin()) |
| break |
| case 'com_android_support_transition': |
| // Not specified in the POM, compileOnly dependency not supposed to be used unless |
| // the library is present: b/70887421 |
| sb.append(' deps += [":com_android_support_support_fragment_java"]\n') |
| break |
| case 'com_android_support_versionedparcelable': |
| 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') |
| // 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("""\ |
| | jar_excluded_patterns = [ |
| | "android/support/v4/graphics/drawable/IconCompat.class", |
| | "androidx/*", |
| | ] |
| | |
| |""".stripMargin()) |
| break |
| case 'com_google_ar_core': |
| // Target .aar file contains .so libraries that need to be extracted, |
| // and android_aar_prebuilt template will fail if it's not set explictly. |
| sb.append(' extract_native_libraries = true\n') |
| break |
| case 'com_google_guava_guava': |
| sb.append('\n') |
| sb.append(' # Need to exclude class and replace it with class library as\n') |
| sb.append(' # com_google_guava_listenablefuture has support_androids=true.\n') |
| sb.append(' deps += [":com_google_guava_listenablefuture_java"]\n') |
| 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') |
| break |
| case 'com_google_code_findbugs_jsr305': |
| case 'com_google_errorprone_error_prone_annotations': |
| case 'com_google_guava_failureaccess': |
| case 'com_google_j2objc_j2objc_annotations': |
| case 'com_google_guava_listenablefuture': |
| case 'com_googlecode_java_diff_utils_diffutils': |
| case 'org_codehaus_mojo_animal_sniffer_annotations': |
| sb.append('\n') |
| sb.append(' # Needed to break dependency cycle for errorprone_plugin_java.\n') |
| sb.append(' enable_bytecode_checks = false\n') |
| break |
| case 'androidx_test_rules': |
| // Target needs Android SDK deps which exist in third_party/android_sdk. |
| 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()) |
| break |
| case 'androidx_test_espresso_espresso_contrib': |
| case 'androidx_test_espresso_espresso_web': |
| case 'androidx_window_window': |
| sb.append(' enable_bytecode_checks = false\n') |
| break |
| case 'net_sf_kxml_kxml2': |
| sb.append(' # Target needs to exclude *xmlpull* files as already included in Android SDK.\n') |
| sb.append(' jar_excluded_patterns = [ "*xmlpull*" ]\n') |
| break |
| case 'androidx_preference_preference': |
| sb.append("""\ |
| | deps += [ "//third_party/android_deps/local_modifications/androidx_preference_preference:androidx_preference_preference_prebuilt_java" ] |
| | # Omit these files since we use our own copy from AndroidX master, included above. |
| | # We can remove this once we migrate to AndroidX master for all libraries. |
| | jar_excluded_patterns = [ |
| | "androidx/preference/PreferenceDialogFragmentCompat*", |
| | "androidx/preference/PreferenceFragmentCompat*", |
| | ] |
| | |
| | bytecode_rewriter_target = "//build/android/bytecode:fragment_activity_replacer" |
| |""".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') |
| break |
| case 'com_google_android_gms_play_services_basement': |
| sb.append(' # https://crbug.com/989505\n') |
| sb.append(' jar_excluded_patterns = ["META-INF/proguard/*"]\n') |
| // Deprecated deps jar but still needed by play services basement. |
| sb.append(' input_jars_paths=["\\$android_sdk/optional/org.apache.http.legacy.jar"]\n') |
| sb.append(' bytecode_rewriter_target = "//build/android/bytecode:fragment_activity_replacer"\n') |
| break |
| case 'com_google_android_gms_play_services_maps': |
| sb.append(' # Ignore the dependency to org.apache.http.legacy. See crbug.com/1084879.\n') |
| 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(' ]') |
| break |
| case 'androidx_webkit_webkit': |
| sb.append(' visibility = [\n') |
| sb.append(' "//android_webview/tools/system_webview_shell:*",\n') |
| sb.append(' "//third_party/android_deps:*"\n') |
| sb.append(' ]') |
| break |
| case 'com_android_tools_desugar_jdk_libs_configuration': |
| sb.append(' enable_bytecode_checks = false\n') |
| break |
| } |
| } |
| |
| private void updateDepsDeclaration(ChromiumDepGraph depGraph, String cipdBucket, |
| String stripFromCipdPath, String repoPath, |
| String depsFilePath) { |
| File depsFile = new File(depsFilePath) |
| def 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") |
| |
| // Comparator to sort the dependencies in alphabetical order. |
| def dependencyComparator = { dependency1, dependency2 -> |
| return dependency1.id.compareTo(dependency2.id) |
| } |
| |
| depGraph.dependencies.values().sort(dependencyComparator).each { dependency -> |
| if (excludeDependency(dependency)) { |
| return |
| } |
| def depPath = "${DOWNLOAD_DIRECTORY_NAME}/${dependency.id}" |
| def cipdPath = "${cipdBucket}/" |
| if (stripFromCipdPath) { |
| assert repoPath.startsWith(stripFromCipdPath) |
| cipdPath += repoPath.substring(stripFromCipdPath.length() + 1) |
| } else { |
| cipdPath += repoPath |
| } |
| // CIPD does not allow uppercase in names. |
| cipdPath += "/${depPath}".toLowerCase() |
| sb.append("""\ |
| | |
| | 'src/${repoPath}/${depPath}': { |
| | 'packages': [ |
| | { |
| | 'package': '${cipdPath}', |
| | 'version': 'version:${dependency.version}-${dependency.cipdSuffix}', |
| | }, |
| | ], |
| | 'condition': 'checkout_android', |
| | 'dep_type': 'cipd', |
| | }, |
| |""".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<String> directories, String readmePath) { |
| File refFile = new File(readmePath) |
| refFile.write(JsonOutput.prettyPrint(JsonOutput.toJson(directories)) + "\n") |
| } |
| |
| public boolean excludeDependency(ChromiumDepGraph.DependencyDescription dependency) { |
| def onlyAndroidx = (repositoryPath == "third_party/androidx") |
| return dependency.exclude || EXISTING_LIBS.get(dependency.id) != null || |
| (onlyPlayServices && !isPlayServicesTarget(dependency.id)) || |
| (onlyAndroidx && !dependency.id.startsWith("androidx_")) || |
| (useDedicatedAndroidxDir && dependency.id == "androidx_legacy_legacy_preference_v14") |
| } |
| |
| private String normalisePath(String pathRelativeToChromiumRoot) { |
| 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" |
| } |
| |
| static String makeReadme(ChromiumDepGraph.DependencyDescription dependency) { |
| def licenseString |
| // Replace license names with ones that are whitelisted, see third_party/PRESUBMIT.py |
| switch (dependency.licenseName) { |
| case "The Apache Software License, Version 2.0": |
| licenseString = "Apache Version 2.0" |
| break |
| default: |
| licenseString = dependency.licenseName |
| } |
| |
| 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 stripFromCipdPath, String repoPath) { |
| if (!stripFromCipdPath) { |
| stripFromCipdPath = '' |
| } |
| def cipdVersion = "${dependency.version}-${dependency.cipdSuffix}" |
| def cipdPath = "${cipdBucket}/" |
| if (stripFromCipdPath) { |
| assert repoPath.startsWith(stripFromCipdPath) |
| cipdPath += repoPath.substring(stripFromCipdPath.length() + 1) |
| } else { |
| cipdPath += repoPath |
| } |
| // CIPD does not allow uppercase in names. |
| cipdPath += "/${DOWNLOAD_DIRECTORY_NAME}/" + dependency.id.toLowerCase() |
| |
| // 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 String jsonDump(obj) { |
| return JsonOutput.prettyPrint(JsonOutput.toJson(obj)) |
| } |
| |
| static void printDump(obj) { |
| getLogger().warn(jsonDump(obj)) |
| } |
| |
| static HttpURLConnection connectAndFollowRedirects(String id, String sourceUrl) { |
| URL urlObj = new URL(sourceUrl) |
| HttpURLConnection connection |
| for (int i = 0; i < 10; ++i) { |
| // Several deps use this URL for their license, but it just points to license |
| // *template*. Generally the actual license can be found in the source code. |
| if (sourceUrl.contains("://opensource.org/licenses")) { |
| throw new RuntimeException("Found templated license URL for dependency " |
| + id + ": " + sourceUrl |
| + ". You will need to edit PROPERTY_OVERRIDES for this dep.") |
| } |
| connection = urlObj.openConnection() |
| switch (connection.getResponseCode()) { |
| case HttpURLConnection.HTTP_MOVED_PERM: |
| case HttpURLConnection.HTTP_MOVED_TEMP: |
| String location = connection.getHeaderField("Location"); |
| urlObj = new URL(urlObj, location); |
| continue |
| case HttpURLConnection.HTTP_OK: |
| return connection |
| default: |
| throw new RuntimeException( |
| "Url had statusCode=" + connection.getResponseCode() + ": " + sourceUrl) |
| } |
| } |
| throw new RuntimeException("Url in redirect loop: " + sourceUrl) |
| } |
| |
| static void downloadFile(String id, String sourceUrl, File destinationFile) { |
| destinationFile.withOutputStream { out -> |
| try { |
| out << connectAndFollowRedirects(id, sourceUrl).getInputStream() |
| } catch (Exception e) { |
| throw new RuntimeException("Failed to fetch license for " + id + " url: " + sourceUrl, e) |
| } |
| } |
| } |
| |
| } |