diff --git a/.github/workflows/stats.yml b/.github/workflows/stats.yml new file mode 100644 index 00000000000..985d8a2ac5d --- /dev/null +++ b/.github/workflows/stats.yml @@ -0,0 +1,216 @@ +# Contains jobs corresponding to stats, including build stats due to changes in a PR. + +name: Stats Checks & Reports + +on: + workflow_dispatch: + schedule: + - cron: "30 02 * * *" + +permissions: + pull-requests: write + +jobs: + find_open_pull_requests: + name: Find open PRs + runs-on: ubuntu-20.04 + outputs: + matrix: ${{ steps.compute-pull-request-matrix.outputs.matrix }} + env: + GH_TOKEN: ${{ github.token }} + steps: + - uses: actions/checkout@v4 + + - name: Compute PR matrix + id: compute-pull-request-matrix + # Remove spaces to ensure the matrix output is on one line. Reference: + # https://stackoverflow.com/a/3232433. + run: | + CURRENT_OPEN_PR_INFO="$(gh pr list --json number,baseRefName,headRefName,headRepository,headRepositoryOwner | tr -d '[:space:]')" + echo "matrix={\"prInfo\": $CURRENT_OPEN_PR_INFO}" >> "$GITHUB_OUTPUT" + + build_stats: + name: Build Stats + needs: find_open_pull_requests + runs-on: ubuntu-20.04 + # Reduce parallelization due to high build times, and allow individual PRs to fail. + strategy: + fail-fast: false + max-parallel: 5 + matrix: ${{ fromJson(needs.find_open_pull_requests.outputs.matrix) }} + env: + ENABLE_CACHING: false + CACHE_DIRECTORY: ~/.bazel_cache + steps: + - name: Compute PR head owner/repo reference + env: + PR_HEAD_REPO: ${{ matrix.prInfo.headRepository.name }} + PR_HEAD_REPO_OWNER: ${{ matrix.prInfo.headRepositoryOwner.login }} + run: | + echo "PR_HEAD=$PR_HEAD_REPO_OWNER/$PR_HEAD_REPO" >> "$GITHUB_ENV" + - name: Print PR information for this run + env: + PR_BASE_REF_NAME: ${{ matrix.prInfo.baseRefName }} + PR_HEAD_REF_NAME: ${{ matrix.prInfo.headRefName }} + PR_NUMBER: ${{ matrix.prInfo.number }} + run: | + echo "PR $PR_NUMBER is merging into $PR_BASE_REF_NAME from https://github.com/$PR_HEAD branch $PR_HEAD_REF_NAME." + + - name: Set up JDK 9 + uses: actions/setup-java@v1 + with: + java-version: 9 + + - name: Set up Bazel + uses: abhinavsingh/setup-bazel@v3 + with: + version: 4.0.0 + + # For reference on this & the later cache actions, see: + # https://github.com/actions/cache/issues/239#issuecomment-606950711 & + # https://github.com/actions/cache/issues/109#issuecomment-558771281. Note that these work + # with Bazel since Bazel can share the most recent cache from an unrelated build and still + # benefit from incremental build performance (assuming that actions/cache aggressively removes + # older caches due to the 5GB cache limit size & Bazel's large cache size). + - uses: actions/cache@v2 + id: cache + with: + path: ${{ env.CACHE_DIRECTORY }} + key: ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-binary-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-binary- + ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel- + + # This check is needed to ensure that Bazel's unbounded cache growth doesn't result in a + # situation where the cache never updates (e.g. due to exceeding GitHub's cache size limit) + # thereby only ever using the last successful cache version. This solution will result in a + # few slower CI actions around the time cache is detected to be too large, but it should + # incrementally improve thereafter. + - name: Ensure cache size + env: + BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }} + run: | + # See https://stackoverflow.com/a/27485157 for reference. + EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}" + CACHE_SIZE_MB=$(du -smc $EXPANDED_BAZEL_CACHE_PATH | grep total | cut -f1) + echo "Total size of Bazel cache (rounded up to MBs): $CACHE_SIZE_MB" + # Use a 4.5GB threshold since actions/cache compresses the results, and Bazel caches seem + # to only increase by a few hundred megabytes across changes for unrelated branches. This + # is also a reasonable upper-bound (local tests as of 2021-03-31 suggest that a full build + # of the codebase (e.g. //...) from scratch only requires a ~2.1GB uncompressed/~900MB + # compressed cache). + if [[ "$CACHE_SIZE_MB" -gt 4500 ]]; then + echo "Cache exceeds cut-off; resetting it (will result in a slow build)" + rm -rf $EXPANDED_BAZEL_CACHE_PATH + fi + + - name: Configure Bazel to use a local cache + env: + BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }} + run: | + EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}" + echo "Using $EXPANDED_BAZEL_CACHE_PATH as Bazel's cache path" + echo "build --disk_cache=$EXPANDED_BAZEL_CACHE_PATH" >> $HOME/.bazelrc + shell: bash + + # This checks out the actual true develop branch separately to ensure that the stats check is + # run from the latest develop rather than the base branch (which might be different for + # chained PRs). + - name: Check out develop repository + uses: actions/checkout@v4 + with: + path: develop + + - name: Set up build environment + uses: ./develop/.github/actions/set-up-android-bazel-build-environment + + - name: Check Bazel environment + run: | + cd develop + bazel info + + - name: Check out base repository and branch + env: + PR_BASE_REF_NAME: ${{ matrix.prInfo.baseRefName }} + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ env.PR_BASE_REF_NAME }} + path: base + + - name: Check out head repository and branch + env: + PR_HEAD_REF_NAME: ${{ matrix.prInfo.headRefName }} + uses: actions/checkout@v4 + with: + fetch-depth: 0 + repository: ${{ env.PR_HEAD }} + ref: ${{ env.PR_HEAD_REF_NAME }} + path: head + + # Note that Bazel is shutdown between builds since multiple Bazel servers will otherwise end + # up being active (due to multiple repositories being used) and this can quickly overwhelm CI + # worker resources. + - name: Build Oppia dev, alpha, beta, and GA (feature branch) + run: | + cd head + git log -n 1 + bazel build -- //:oppia_dev //:oppia_alpha //:oppia_beta //:oppia_ga + cp bazel-bin/oppia_dev.aab ../develop/oppia_dev_with_changes.aab + cp bazel-bin/oppia_alpha.aab ../develop/oppia_alpha_with_changes.aab + cp bazel-bin/oppia_beta.aab ../develop/oppia_beta_with_changes.aab + cp bazel-bin/oppia_ga.aab ../develop/oppia_ga_with_changes.aab + bazel shutdown + + - name: Build Oppia dev, alpha, beta, and GA (base branch) + run: | + cd base + git log -n 1 + bazel build -- //:oppia_dev //:oppia_alpha //:oppia_beta //:oppia_ga + cp bazel-bin/oppia_dev.aab ../develop/oppia_dev_without_changes.aab + cp bazel-bin/oppia_alpha.aab ../develop/oppia_alpha_without_changes.aab + cp bazel-bin/oppia_beta.aab ../develop/oppia_beta_without_changes.aab + cp bazel-bin/oppia_ga.aab ../develop/oppia_ga_without_changes.aab + bazel shutdown + + - name: Run stats analysis tool (develop branch) + run: | + cd develop + git log -n 1 + bazel run //scripts:compute_aab_differences -- \ + $(pwd)/brief_build_summary.log $(pwd)/full_build_summary.log \ + dev $(pwd)/oppia_dev_without_changes.aab $(pwd)/oppia_dev_with_changes.aab \ + alpha $(pwd)/oppia_alpha_without_changes.aab $(pwd)/oppia_alpha_with_changes.aab \ + beta $(pwd)/oppia_beta_without_changes.aab $(pwd)/oppia_beta_with_changes.aab \ + ga $(pwd)/oppia_ga_without_changes.aab $(pwd)/oppia_ga_with_changes.aab + + # Reference: https://github.com/peter-evans/create-or-update-comment#setting-the-comment-body-from-a-file. + # Also, for multi-line env values, see: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings. + - name: Extract reports for uploading & commenting + env: + PR_NUMBER: ${{ matrix.prInfo.number }} + id: compute-comment-body + run: | + { + echo 'comment_body<> "$GITHUB_OUTPUT" + FULL_BUILD_SUMMARY_FILE_NAME="full_build_summary_pr_$PR_NUMBER.log" + FULL_BUILD_SUMMARY_FILE_PATH="$GITHUB_WORKSPACE/develop/$FULL_BUILD_SUMMARY_FILE_NAME" + echo "FULL_BUILD_SUMMARY_FILE_NAME=$FULL_BUILD_SUMMARY_FILE_NAME" >> "$GITHUB_ENV" + echo "FULL_BUILD_SUMMARY_FILE_PATH=$FULL_BUILD_SUMMARY_FILE_PATH" >> "$GITHUB_ENV" + cp "$GITHUB_WORKSPACE/develop/full_build_summary.log" "$FULL_BUILD_SUMMARY_FILE_PATH" + + - name: Add build stats summary comment + env: + PR_NUMBER: ${{ matrix.prInfo.number }} + uses: peter-evans/create-or-update-comment@v1 + with: + issue-number: ${{ env.PR_NUMBER }} + body: ${{ steps.compute-comment-body.outputs.comment_body }} + + - uses: actions/upload-artifact@v2 + with: + name: ${{ env.FULL_BUILD_SUMMARY_FILE_NAME }} + path: ${{ env.FULL_BUILD_SUMMARY_FILE_PATH }} diff --git a/WORKSPACE b/WORKSPACE index dc4b29ece77..1232f4971ea 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -4,6 +4,7 @@ This file lists and imports all external dependencies needed to build Oppia Andr load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_jar") +load("//:build_vars.bzl", "BUILD_SDK_VERSION", "BUILD_TOOLS_VERSION") load("//third_party:versions.bzl", "HTTP_DEPENDENCY_VERSIONS", "MAVEN_REPOSITORIES", "get_maven_dependencies") # Android SDK configuration. For more details, see: @@ -11,8 +12,8 @@ load("//third_party:versions.bzl", "HTTP_DEPENDENCY_VERSIONS", "MAVEN_REPOSITORI # TODO(#1542): Sync Android SDK version with the manifest. android_sdk_repository( name = "androidsdk", - api_level = 33, - build_tools_version = "29.0.2", + api_level = BUILD_SDK_VERSION, + build_tools_version = BUILD_TOOLS_VERSION, ) # Oppia's backend proto API definitions. @@ -160,6 +161,13 @@ git_repository( shallow_since = "1679426649 -0700", ) +git_repository( + name = "archive_patcher", + commit = "d1c18b0035d5f669ddaefadade49cae0748f9df2", + remote = "https://github.com/oppia/archive-patcher", + shallow_since = "1642022460 -0800", +) + bind( name = "databinding_annotation_processor", actual = "//tools/android:compiler_annotation_processor", @@ -214,17 +222,24 @@ load("@maven//:defs.bzl", "pinned_maven_install") pinned_maven_install() -http_jar( - name = "guava_android", - sha256 = HTTP_DEPENDENCY_VERSIONS["guava_android"]["sha"], - urls = [ - "{0}/com/google/guava/guava/{1}-android/guava-{1}-android.jar".format( - url_base, - HTTP_DEPENDENCY_VERSIONS["guava_android"]["version"], - ) - for url_base in DAGGER_REPOSITORIES + MAVEN_REPOSITORIES - ], -) +[ + http_jar( + name = "guava_%s" % guava_type, + sha256 = HTTP_DEPENDENCY_VERSIONS["guava_%s" % guava_type]["sha"], + urls = [ + "{0}/com/google/guava/guava/{1}-{2}/guava-{1}-{2}.jar".format( + url_base, + HTTP_DEPENDENCY_VERSIONS["guava_%s" % guava_type]["version"], + guava_type, + ) + for url_base in DAGGER_REPOSITORIES + MAVEN_REPOSITORIES + ], + ) + for guava_type in [ + "android", + "jre", + ] +] http_jar( name = "kotlinx-coroutines-core-jvm", diff --git a/build_vars.bzl b/build_vars.bzl new file mode 100644 index 00000000000..99b12eef5ae --- /dev/null +++ b/build_vars.bzl @@ -0,0 +1,2 @@ +BUILD_SDK_VERSION = 33 +BUILD_TOOLS_VERSION = "29.0.2" diff --git a/oppia_android_application.bzl b/oppia_android_application.bzl index e941becc4a4..d9951cefa18 100644 --- a/oppia_android_application.bzl +++ b/oppia_android_application.bzl @@ -280,7 +280,7 @@ _bundle_module_zip_into_deployable_aab = rule( "_bundletool_tool": attr.label( executable = True, cfg = "host", - default = "//third_party:android_bundletool", + default = "//third_party:android_bundletool_binary", ), }, implementation = _bundle_module_zip_into_deployable_aab_impl, @@ -316,7 +316,7 @@ _generate_apks_and_install = rule( "_bundletool_tool": attr.label( executable = True, cfg = "host", - default = "//third_party:android_bundletool", + default = "//third_party:android_bundletool_binary", ), }, executable = True, diff --git a/scripts/BUILD.bazel b/scripts/BUILD.bazel index 2199e663d4e..689cf6e53d2 100644 --- a/scripts/BUILD.bazel +++ b/scripts/BUILD.bazel @@ -42,6 +42,16 @@ package_group( packages = ["//scripts/src/java/..."], ) +kt_jvm_binary( + name = "compute_aab_differences", + testonly = True, + data = ["@androidsdk//:aapt2_binary"], + main_class = "org.oppia.android.scripts.apkstats.ComputeAabDifferencesKt", + runtime_deps = [ + "//scripts/src/java/org/oppia/android/scripts/apkstats:compute_aab_differences_lib", + ], +) + kt_jvm_binary( name = "compute_affected_tests", testonly = True, diff --git a/scripts/src/java/org/oppia/android/scripts/apkstats/Aapt2Client.kt b/scripts/src/java/org/oppia/android/scripts/apkstats/Aapt2Client.kt new file mode 100644 index 00000000000..6e00ea41d79 --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/apkstats/Aapt2Client.kt @@ -0,0 +1,69 @@ +package org.oppia.android.scripts.apkstats + +import org.oppia.android.scripts.common.CommandExecutor +import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher +import java.io.File + +/** + * General utility for interfacing with AAPT2 in the local system at the specified working directory + * path and contained within the specified Android SDK (per the given path). + * + * Note that in order for binary dependencies to utilize this client, they must add a 'data' + * dependency on the AAPT2 binary included as part of the Android SDK, e.g.: + * + * ```bazel + * data = ["@androidsdk//:aapt2_binary"] + * ``` + * + * @property workingDirectoryPath the path to the working directory in which instances of AAPT2 + * should be executed + * @property buildToolsVersion the version of Android build tools installed & that should be used. + * This value should be coordinated with the build system used by the APKs accessed by this + * utility. + * @param scriptBgDispatcher the [ScriptBackgroundCoroutineDispatcher] to be used for running the + * AAPT2 command + * @property commandExecutor the [CommandExecutor] to use when accessing AAPT2 + */ +class Aapt2Client( + private val workingDirectoryPath: String, + private val buildToolsVersion: String, + scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher, + private val commandExecutor: CommandExecutor = CommandExecutorImpl(scriptBgDispatcher) +) { + private val workingDirectory by lazy { File(workingDirectoryPath) } + // Note that this pathing will not work by default on Windows (since executables end with '.exe'). + private val aapt2Path by lazy { + File("external/androidsdk", "build-tools/$buildToolsVersion/aapt2").absolutePath + } + + // CLI reference: https://developer.android.com/studio/command-line/apkanalyzer. + + /** Returns the permissions dump as reported by AAPT2 for the specified APK. */ + fun dumpPermissions(inputApkPath: String): List { + return executeApkAnalyzerCommand("dump", "permissions", inputApkPath) + } + + /** Returns the resources dump as reported by AAPT2 for the specified APK. */ + fun dumpResources(inputApkPath: String): List { + return executeApkAnalyzerCommand("dump", "resources", inputApkPath) + } + + /** + * Returns badging information, that is, high-level details like supported locales and densities, + * from the specified APK's manifest. + */ + fun dumpBadging(inputApkPath: String): List { + return executeApkAnalyzerCommand("dump", "badging", inputApkPath) + } + + private fun executeApkAnalyzerCommand(vararg arguments: String): List { + val result = commandExecutor.executeCommand(workingDirectory, aapt2Path, *arguments) + check(result.exitCode == 0) { + "Expected zero exit code (not ${result.exitCode}) for command: ${result.command}." + + "\nStandard output:\n${result.output.joinToString("\n")}" + + "\nError output:\n${result.errorOutput.joinToString("\n")}" + } + return result.output + } +} diff --git a/scripts/src/java/org/oppia/android/scripts/apkstats/ApkAnalyzerClient.kt b/scripts/src/java/org/oppia/android/scripts/apkstats/ApkAnalyzerClient.kt new file mode 100644 index 00000000000..e24a8620318 --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/apkstats/ApkAnalyzerClient.kt @@ -0,0 +1,105 @@ +package org.oppia.android.scripts.apkstats + +import com.android.tools.apk.analyzer.AndroidApplicationInfo +import com.android.tools.apk.analyzer.ApkSizeCalculator +import com.android.tools.apk.analyzer.Archives +import com.android.tools.apk.analyzer.dex.DexFileStats +import com.android.tools.apk.analyzer.dex.DexFiles +import com.android.tools.apk.analyzer.internal.ApkDiffEntry +import com.android.tools.apk.analyzer.internal.ApkFileByFileDiffParser +import java.io.File +import java.util.zip.ZipFile +import javax.swing.tree.DefaultMutableTreeNode + +/** + * General utility for interfacing with apkanalyzer's internal library. This implementation + * generally behaves like the apkanalyzer CLI implementation with some simplification. Note that the + * actual apkanalyzer tool can't be used since it isn't exported by android_sdk_repository. + * + * @property aapt2Client the [Aapt2Client] needed to access aapt2 by some routines + */ +class ApkAnalyzerClient(private val aapt2Client: Aapt2Client) { + private val apkSizeCalculator by lazy { ApkSizeCalculator.getDefault() } + + // CLI reference: https://developer.android.com/studio/command-line/apkanalyzer. + + /** Returns the file size of the specified APK as similarly reported by apkanalyzer. */ + fun computeFileSize(inputApkPath: String): Long = + apkSizeCalculator.getFullApkRawSize(File(inputApkPath).toPath()) + + /** + * Returns the estimated download size of the specified APK as similarly reported by apkanalyzer. + */ + fun computeDownloadSize(inputApkPath: String): Long = + apkSizeCalculator.getFullApkDownloadSize(File(inputApkPath).toPath()) + + /** + * Returns the list of required features of the specified APK as similarly reported by + * apkanalyzer. + */ + fun computeFeatures(inputApkPath: String): List { + val apkInfo = AndroidApplicationInfo.parseBadging(aapt2Client.dumpBadging(inputApkPath)) + return apkInfo.usesFeature.keys.toList() + } + + /** + * Returns a full comparison of the two specified APKs, similar to apkanalyzer. Note that each + * entry of the returned list represents a single line (the list is in the order of the comparison + * output). + */ + fun compare(inputApkPath1: String, inputApkPath2: String): List { + return Archives.open(File(inputApkPath1).toPath()).use { apkArchive1 -> + Archives.open(File(inputApkPath2).toPath()).use { apkArchive2 -> + walkParseTree(ApkFileByFileDiffParser.createTreeNode(apkArchive1, apkArchive2)) + } + } + } + + /** + * Returns the map of dex files to method counts contained within the specified APK file, as + * similarly reported by apkanalyzer. + */ + fun computeDexReferencesList(inputApkPath: String): Map { + return collectZipEntries(inputApkPath) { + it.endsWith(".dex") + }.mapValues { (_, dexFileContents) -> + DexFileStats.create(listOf(DexFiles.getDexFile(dexFileContents))).referencedMethodCount + } + } + + // Based on the apkanalyzer CLI implementation. + private fun walkParseTree(node: DefaultMutableTreeNode?): List { + return node?.let { + (node.userObject as? ApkDiffEntry)?.let { entry -> + val path = entry.path.toString() + when { + // Encountered a folder. Recurse down it in case its sizes aren't accurate (descendants + // may have changed). + path.endsWith("/") -> { + (0 until node.getChildCount()) + .map { node.getChildAt(it) } + .flatMap { walkParseTree(it as? DefaultMutableTreeNode) } + } + // Only record the entry if there's a difference. + entry.oldSize != entry.newSize -> { + listOf("${entry.oldSize}\t${entry.newSize}\t${entry.size}\t${entry.path}") + } + else -> null + } + } + } ?: listOf() + } + + private fun collectZipEntries( + inputZipFile: String, + namePredicate: (String) -> Boolean + ): Map { + return ZipFile(inputZipFile).use { zipFile -> + zipFile.entries() + .asSequence() + .filter { namePredicate(it.name) } + .associateBy { it.name } + .mapValues { (_, entry) -> zipFile.getInputStream(entry).readBytes() } + } + } +} diff --git a/scripts/src/java/org/oppia/android/scripts/apkstats/BUILD.bazel b/scripts/src/java/org/oppia/android/scripts/apkstats/BUILD.bazel new file mode 100644 index 00000000000..7730a9c084a --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/apkstats/BUILD.bazel @@ -0,0 +1,64 @@ +""" +Libraries corresponding to developer scripts that help with computing APK & AAB stats. +""" + +load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") + +kt_jvm_library( + name = "compute_aab_differences_lib", + testonly = True, + srcs = [ + "ComputeAabDifferences.kt", + ], + visibility = ["//scripts:oppia_script_binary_visibility"], + deps = [ + ":aapt2_client", + ":apk_analyzer_client", + ":bundle_tool_client", + "//scripts/src/java/org/oppia/android/scripts/common:android_build_sdk_properties", + ], +) + +kt_jvm_library( + name = "aapt2_client", + testonly = True, + srcs = [ + "Aapt2Client.kt", + ], + visibility = ["//scripts:oppia_script_test_visibility"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/common:command_executor", + ], +) + +kt_jvm_library( + name = "apk_analyzer_client", + testonly = True, + srcs = [ + "ApkAnalyzerClient.kt", + ], + visibility = ["//scripts:oppia_script_test_visibility"], + runtime_deps = [ + "//third_party:com_google_archivepatcher", + ], + deps = [ + ":aapt2_client", + "//third_party:com_android_tools_apkparser_apkanalyzer", + ], +) + +kt_jvm_library( + name = "bundle_tool_client", + testonly = True, + srcs = [ + "BundleToolClient.kt", + ], + visibility = ["//scripts:oppia_script_test_visibility"], + runtime_deps = [ + "//third_party:android_bundletool", + "//third_party:com_google_guava_guava_jre", + ], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/common:command_executor", + ], +) diff --git a/scripts/src/java/org/oppia/android/scripts/apkstats/BundleToolClient.kt b/scripts/src/java/org/oppia/android/scripts/apkstats/BundleToolClient.kt new file mode 100644 index 00000000000..5c7ed73d26a --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/apkstats/BundleToolClient.kt @@ -0,0 +1,130 @@ +package org.oppia.android.scripts.apkstats + +import org.oppia.android.scripts.common.CommandExecutor +import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher +import java.io.File +import java.util.zip.ZipFile + +/** + * General utility for interfacing with bundletool in the local system at the specified working + * directory path. + * + * @property workingDirectoryPath the path to the working directory in which instances of bundletool + * should be executed + * @param scriptBgDispatcher the [ScriptBackgroundCoroutineDispatcher] to be used for running the + * bundletool command + * @property commandExecutor the [CommandExecutor] to use when accessing bundletool + */ +class BundleToolClient( + private val workingDirectoryPath: String, + scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher, + private val commandExecutor: CommandExecutor = CommandExecutorImpl(scriptBgDispatcher) +) { + private val workingDirectory by lazy { File(workingDirectoryPath) } + + // CLI reference: https://developer.android.com/studio/command-line/bundletool. + + /** + * Builds & extracts configuration-specific APKs corresponding to the specified Android app + * bundle. + * + * @param inputBundlePath the AAB from which to extract APKs + * @param outputApksListPath the destination .apks file intermediary used to extract the APKs + * @param outputApkDirPath the destination directory in which the extracted APKs should be written + * @return the list of [File]s where each corresponds to one of the computed APKs + */ + fun buildApks( + inputBundlePath: String, + outputApksListPath: String, + outputApkDirPath: String + ): List { + val destDir = File(outputApkDirPath) + return buildApkList(inputBundlePath, outputApksListPath).use { zipFile -> + val apkEntries = + zipFile.entries() + .asSequence() + .filter { !it.isDirectory && it.name.endsWith(".apk", ignoreCase = true) } + return@use apkEntries.map { entry -> + val outputApkFile = File(destDir, entry.name.substringAfter('/')) + zipFile.extractTo(entry.name, outputApkFile.absolutePath) + }.toList() + } + } + + /** + * Builds and returns the file to a universal APK built in the specified output directory path and + * built according to the specified Android app bundle. + */ + fun buildUniversalApk(inputBundlePath: String, outputApkPath: String): File { + return buildApkList(inputBundlePath, "$outputApkPath.apks", "--mode=universal").use { zipFile -> + zipFile.extractTo("universal.apk", outputApkPath) + } + } + + private fun buildApkList( + inputBundlePath: String, + outputApksListPath: String, + vararg additionalArgs: String + ): ZipFile { + executeBundleToolCommand( + "build-apks", "--bundle=$inputBundlePath", "--output=$outputApksListPath", *additionalArgs + ) + return ZipFile(File(outputApksListPath)) + } + + private fun executeBundleToolCommand(vararg arguments: String): List { + // Reference for retaining the classpath: https://stackoverflow.com/a/4330928. Note that this + // approach is needed vs. using reflection since bundle tool seems to not allow multiple + // subsequent calls (so each call must be in a new process). + val result = + commandExecutor.executeCommand( + workingDirectory, + "java", + "-classpath", + computeAbsoluteClasspath(), + "com.android.tools.build.bundletool.BundleToolMain", + *arguments + ) + check(result.exitCode == 0) { + "Expected zero exit code (not ${result.exitCode}) for command: ${result.command}." + + "\nStandard output:\n${result.output.joinToString("\n")}" + + "\nError output:\n${result.errorOutput.joinToString("\n")}" + } + return result.output + } + + private companion object { + private val currentDirectory by lazy { File(".") } + + private fun ZipFile.extractTo(entryName: String, destPath: String): File { + val destFile = File(destPath) + destFile.outputStream().use { outputStream -> + getInputStream(getEntry(entryName)).copyTo(outputStream) + } + return destFile + } + + private fun computeAbsoluteClasspath(): String { + val classpath = System.getProperty("java.class.path") ?: "." + val classpathComponents = classpath.split(":") + return classpathComponents.map { + it.convertToAbsolutePath() + }.filterNot { + it.isAndroidDependencyToOmit() + }.joinToString(":") + } + + private fun String.convertToAbsolutePath(): String { + return File(currentDirectory, this).absolutePath + } + + private fun String.isAndroidDependencyToOmit(): Boolean { + // This is a hacky way to work around the classpath actually pulling in two versions of + // Guava: Android & JRE. Bundle tool requires the JRE version, and there's no obvious way to + // separate out the Maven dependencies without risking duplicate versions & automatic conflict + // resolution. + return "guava_android" in File(this).absolutePath + } + } +} diff --git a/scripts/src/java/org/oppia/android/scripts/apkstats/ComputeAabDifferences.kt b/scripts/src/java/org/oppia/android/scripts/apkstats/ComputeAabDifferences.kt new file mode 100644 index 00000000000..a93dd9ad791 --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/apkstats/ComputeAabDifferences.kt @@ -0,0 +1,798 @@ +package org.oppia.android.scripts.apkstats + +import org.oppia.android.scripts.common.AndroidBuildSdkProperties +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher +import java.io.File +import java.io.PrintStream +import java.nio.file.Files +import java.util.Locale +import java.util.StringTokenizer +import java.util.zip.ZipFile +import kotlin.math.absoluteValue + +// TODO(#1719): Add support for showing count & itemization of modified files/resources (vs. just +// new/removed). + +/** + * The main entrypoint for analyzing different builds of the app and computing stat differences. + * + * Usage: + * bazel run //scripts:compute_aab_differences -- \\ + * \\ + * [ \\ + * ] ... + * + * Arguments: + * - path_to_brief_summary_output_file: path to the file that will contain a brief difference + * summary. + * - path_to_full_summary_output_file: path to the file that will contain a more detailed difference + * summary. + * - One or more triplets containing: + * - build_flavor: the flavor of the build corresponding to this quartet (e.g. alpha). + * - path_to_flavor_input_without_changes: path to the built AAB for this flavor that doesn't + * contain the changes to analyze (e.g. built on develop). + * - path_to_flavor_input_with_changes: path to the built AAB for this flavor that includes the + * changes to analyze. + * + * Example: + * bazel run //scripts:compute_aab_differences -- \\ + * $(pwd)/brief_build_summary.log $(pwd)/full_build_summary.log \\ + * dev $(pwd)/dev_no_changes.aab $(pwd)/dev_with_changes.aab \\ + * alpha $(pwd)/alpha_no_changes.aab $(pwd)/alpha_with_changes.aab + */ +fun main(vararg args: String) { + val outputSummaryFilePath = args[0] + val outputFullSummaryFilePath = args[1] + + val remainingArgCount = args.size - 2 + check(remainingArgCount > 0 && (remainingArgCount % 3) == 0) { + "Expected at least 1 triplet entry of the form: " + } + val profiles = + args.drop(2).chunked(3).map { (flavor, aabNoChangesPath, aabWithChangesPath) -> + ComputeAabDifferences.AabProfile( + buildFlavor = flavor, + oldAabFilePath = aabNoChangesPath, + newAabFilePath = aabWithChangesPath + ) + } + + println("NOTE: Computing ${profiles.size} build flavor stats profiles.") + + val workingDirectoryPath = "." + val sdkProperties = AndroidBuildSdkProperties() + val buildStats = ScriptBackgroundCoroutineDispatcher().use { scriptBgDispatcher -> + val computer = ComputeAabDifferences(workingDirectoryPath, sdkProperties, scriptBgDispatcher) + return@use computer.computeBuildStats(*profiles.toTypedArray()) + } + PrintStream(outputSummaryFilePath).use { stream -> + buildStats.writeSummariesTo(stream, longSummary = false) + } + PrintStream(outputFullSummaryFilePath).use { stream -> + buildStats.writeSummariesTo(stream, longSummary = true) + } +} + +/** Utility to compute the build differences between sets of AABs. */ +class ComputeAabDifferences( + workingDirectoryPath: String, + sdkProperties: AndroidBuildSdkProperties, + scriptBgDispatcher: ScriptBackgroundCoroutineDispatcher +) { + private val bundleToolClient by lazy { + BundleToolClient(workingDirectoryPath, scriptBgDispatcher) + } + private val aapt2Client by lazy { + Aapt2Client(workingDirectoryPath, sdkProperties.buildToolsVersion, scriptBgDispatcher) + } + private val apkAnalyzerClient by lazy { ApkAnalyzerClient(aapt2Client) } + + /** + * Returns the [BuildStats] for the provided set of [AabProfile]s. All profiles will be + * represented in the returned stats. + */ + fun computeBuildStats(vararg aabProfiles: AabProfile): BuildStats { + val aabStats = aabProfiles.associate { profile -> + profile.buildFlavor to computeAabStats(profile.oldAabFilePath, profile.newAabFilePath) + } + return BuildStats(aabStats) + } + + private fun computeAabStats(aabWithoutChangesPath: String, aabWithChangesPath: String): AabStats { + println( + "Computing AAB stats for AABs: $aabWithoutChangesPath (old) and $aabWithChangesPath (new)..." + ) + val parentDestDir = Files.createTempDirectory("apk_diff_stats_").toFile() + println("Using ${parentDestDir.absolutePath} as an intermediary working directory") + val destWithoutChanges = parentDestDir.newDirectory("without_changes").absolutePath + val destWithChanges = parentDestDir.newDirectory("with_changes").absolutePath + + val universalApkWithoutChanges = computeUniversalApk(aabWithoutChangesPath, destWithoutChanges) + val universalApkWithChanges = computeUniversalApk(aabWithChangesPath, destWithChanges) + + val (mainApkWithoutChanges, splitApksWithoutChanges) = + computeApksList(aabWithoutChangesPath, destWithoutChanges) + val (mainApkWithChanges, splitApksWithChanges) = + computeApksList(aabWithChangesPath, destWithChanges) + val splitApkStats = combineMaps( + splitApksWithoutChanges, splitApksWithChanges + ) { fileWithoutChangesPath, fileWithChangesPath -> + computeConfigurationStats(fileWithoutChangesPath, fileWithChangesPath) + } + val configurationsList = + DiffList(splitApksWithoutChanges.keys.toList(), splitApksWithChanges.keys.toList()) + + return AabStats( + universalApkStats = computeConfigurationStats( + universalApkWithoutChanges, universalApkWithChanges + ), + mainSplitApkStats = computeConfigurationStats( + mainApkWithoutChanges, mainApkWithChanges + ), + splitApkStats = splitApkStats, + configurationsList = configurationsList + ) + } + + private fun computeUniversalApk(inputAabPath: String, destDir: String): String { + println("Generating universal APK for: $inputAabPath") + val universalApkPath = File(destDir, "universal.apk").absolutePath + val universalApk = bundleToolClient.buildUniversalApk(inputAabPath, universalApkPath) + return universalApk.absolutePath + } + + private fun computeApksList( + inputAabPath: String, + destDir: String + ): Pair> { + println("Generating APK list for: $inputAabPath") + val apksListPath = File(destDir, "list.apks").absolutePath + val apkFiles = bundleToolClient.buildApks(inputAabPath, apksListPath, destDir) + val mainApkFilePath = apkFiles.first { "master" in it.name }.absolutePath + val apkFilePathMap = + apkFiles.filter { "master" !in it.name } + .associate { file -> + file.name.substringAfter("base-").substringBefore('.') to file.absolutePath + } + return mainApkFilePath to apkFilePathMap + } + + private fun computeConfigurationStats( + apkWithoutChangesPath: String?, + apkWithChangesPath: String? + ): ApkConfigurationStats { + println("Comparing APKs: $apkWithoutChangesPath and $apkWithChangesPath") + val fullComparison = if (apkWithoutChangesPath != null && apkWithChangesPath != null) { + apkAnalyzerClient.compare(apkWithoutChangesPath, apkWithChangesPath) + } else null + return ApkConfigurationStats( + fileSizeStats = computeFileSizeStats(apkWithoutChangesPath, apkWithChangesPath), + dexStats = computeDexStats(apkWithoutChangesPath, apkWithChangesPath), + manifestStats = computeManifestStats(apkWithoutChangesPath, apkWithChangesPath), + resourceStats = computeResourceStats(apkWithoutChangesPath, apkWithChangesPath), + assetStats = computeAssetStats(apkWithoutChangesPath, apkWithChangesPath), + completeFileDiff = fullComparison + ) + } + + private fun computeFileSizeStats( + apkWithoutChangesPath: String?, + apkWithChangesPath: String? + ): FileSizeStats { + println("Computing file size for: $apkWithoutChangesPath and $apkWithChangesPath") + val (fileSizeWithoutChanges, downloadSizeWithoutChanges) = if (apkWithoutChangesPath != null) { + apkAnalyzerClient.computeFileSize(apkWithoutChangesPath) to + apkAnalyzerClient.computeDownloadSize(apkWithoutChangesPath) + } else 0L to 0L + + println("Computing estimated download size for: $apkWithoutChangesPath and $apkWithChangesPath") + val (fileSizeWithChanges, downloadSizeWithChanges) = if (apkWithChangesPath != null) { + apkAnalyzerClient.computeFileSize(apkWithChangesPath) to + apkAnalyzerClient.computeDownloadSize(apkWithChangesPath) + } else 0L to 0L + + return FileSizeStats( + fileSize = DiffLong(oldValue = fileSizeWithoutChanges, newValue = fileSizeWithChanges), + downloadSize = DiffLong( + oldValue = downloadSizeWithoutChanges, newValue = downloadSizeWithChanges + ) + ) + } + + private fun computeDexStats( + apkWithoutChangesPath: String?, + apkWithChangesPath: String? + ): DexStats { + println("Computing dex method counts for: $apkWithoutChangesPath and $apkWithChangesPath") + val methodCountWithoutChanges = apkWithoutChangesPath?.let { apkPath -> + apkAnalyzerClient.computeDexReferencesList(apkPath).values.sum().toLong() + } ?: 0L + val methodCountWithChanges = apkWithChangesPath?.let { apkPath -> + apkAnalyzerClient.computeDexReferencesList(apkPath).values.sum().toLong() + } ?: 0L + return DexStats( + DiffLong(oldValue = methodCountWithoutChanges, newValue = methodCountWithChanges) + ) + } + + private fun computeManifestStats( + apkWithoutChangesPath: String?, + apkWithChangesPath: String? + ): ManifestStats { + println("Computing feature and permissions for: $apkWithoutChangesPath and $apkWithChangesPath") + val (featuresWithoutChanges, permissionsWithoutChanges) = apkWithoutChangesPath?.let { path -> + val features = apkAnalyzerClient.computeFeatures(path) + val rawPermissions = aapt2Client.dumpPermissions(path) + return@let features to extractPermissions(rawPermissions) + } ?: (listOf() to listOf()) + val (featuresWithChanges, permissionsWithChanges) = apkWithChangesPath?.let { path -> + val features = apkAnalyzerClient.computeFeatures(path) + val rawPermissions = aapt2Client.dumpPermissions(path) + return@let features to extractPermissions(rawPermissions) + } ?: (listOf() to listOf()) + + return ManifestStats( + features = DiffList(featuresWithoutChanges, featuresWithChanges), + permissions = DiffList(permissionsWithoutChanges, permissionsWithChanges) + ) + } + + private fun computeResourceStats( + apkWithoutChangesPath: String?, + apkWithChangesPath: String? + ): ResourceStats { + println("Computing resource maps for: $apkWithoutChangesPath and $apkWithChangesPath") + val resourcesWithoutChanges = apkWithoutChangesPath?.let { apkPath -> + extractResources(aapt2Client.dumpResources(apkPath)) + } ?: mapOf() + val resourcesWithChanges = apkWithChangesPath?.let { apkPath -> + extractResources(aapt2Client.dumpResources(apkPath)) + } ?: mapOf() + return ResourceStats(combineResourceMaps(resourcesWithoutChanges, resourcesWithChanges)) + } + + private fun computeAssetStats( + apkWithoutChangesPath: String?, + apkWithChangesPath: String? + ): AssetStats { + // Only consider top-level files in the assets/ folder. + println("Computing asset stats for: $apkWithoutChangesPath and $apkWithChangesPath") + return AssetStats( + DiffList( + apkWithoutChangesPath?.let(::File)?.extractAssetFileNamesFromApk() ?: listOf(), + apkWithChangesPath?.let(::File)?.extractAssetFileNamesFromApk() ?: listOf() + ) + ) + } + + /** + * Represents the AABs corresponding to a single build flavor. + * + * @property buildFlavor the name of the build flavor + * @property oldAabFilePath the path to the AAB build of this flavor that doesn't include changes + * to analyze + * @property newAabFilePath the path to the AAB build of this flavor that includes changes to + * analyze + */ + data class AabProfile( + val buildFlavor: String, + val oldAabFilePath: String, + val newAabFilePath: String + ) + + /** + * Represents the computed build stats for multiple build flavors. + * + * @property aabStats a map from build flavor to [AabStats] corresponding to a list of + * [AabProfile]s from and for which build stats were computed + */ + data class BuildStats(val aabStats: Map) { + /** + * Writes the build stats summary to [stream] with a configurable length using [longSummary]. + */ + fun writeSummariesTo(stream: PrintStream, longSummary: Boolean) { + stream.println("# APK & AAB differences analysis") + if (!longSummary) { + stream.println( + "Note that this is a summarized snapshot. See the CI artifacts for detailed" + + " differences." + ) + } + val itemLimit = if (!longSummary) 5 else Int.MAX_VALUE + aabStats.forEach { (buildFlavor, stats) -> + stream.println() + stats.writeSummaryTo(stream, buildFlavor, itemLimit, longSummary) + } + } + } + + /** + * Difference stats between two AABs for a single build flavor. + * + * @property universalApkStats stats corresponding to the before & after universal APKs built for + * the considered build flavor + * @property mainSplitApkStats stats corresponding to the base APK of the compared AABs for the + * considered build flavor + * @property splitApkStats a map from configuration name to the compared stats for each split APK + * of the compared AABs for the considered build flavor + * @property configurationsList a difference list to compare the available AAB configurations for + * both AABs of the considered build flavor + */ + data class AabStats( + val universalApkStats: ApkConfigurationStats, + val mainSplitApkStats: ApkConfigurationStats, + val splitApkStats: Map, + val configurationsList: DiffList + ) { + /** + * Writes the stats summary between two AABs to [stream]. + * + * @param buildFlavor the build flavor corresponding to the compared AABs + * @param itemLimit the max number of items to include in expanded lists (only used if + * [longSummary] is true) + * @param longSummary whether to print a more detailed summary + */ + fun writeSummaryTo( + stream: PrintStream, + buildFlavor: String, + itemLimit: Int, + longSummary: Boolean + ) { + val buildFlavorTitle = buildFlavor.replaceFirstChar { + if (it.isLowerCase()) it.titlecase(Locale.US) else it.toString() + } + stream.println("## $buildFlavorTitle") + stream.println() + + if (!longSummary) { + stream.println("
Expand to see flavor specifics") + stream.println() + } + + stream.println("### Universal APK") + universalApkStats.writeSummaryTo(stream, itemize = true, longSummary, itemLimit) + + stream.println("### AAB differences") + if (!longSummary) { + stream.println("
Expand to see AAB specifics") + stream.println() + } + stream.println("Supported configurations:") + configurationsList.forEach { (type, configuration) -> + stream.println("- $configuration (${type.humanReadableName})") + } + stream.println() + + stream.println("#### Base APK") + mainSplitApkStats.writeSummaryTo(stream, itemize = longSummary, longSummary, itemLimit) + + splitApkStats.forEach { (configuration, stats) -> + stream.println() + stream.println("#### Configuration $configuration") + stats.writeSummaryTo(stream, itemize = longSummary, longSummary, itemLimit) + } + if (!longSummary) stream.println("
") + } + } + + /** + * Enumerated stats demonstrating the high-level differences between two APKs. + * + * @property fileSizeStats file stats comparison between the two APKs + * @property dexStats dex stats comparison between the two APKs + * @property manifestStats manifest stats comparison between the two APKs + * @property resourceStats resource stats comparison between the two APKs + * @property assetStats asset stats comparison between the two APKs + * @property completeFileDiff the lines of the full comparison output between the two APKs, or + * null if none is computed for this APK + */ + data class ApkConfigurationStats( + val fileSizeStats: FileSizeStats, + val dexStats: DexStats, + val manifestStats: ManifestStats, + val resourceStats: ResourceStats, + val assetStats: AssetStats, + val completeFileDiff: List? + ) { + /** + * Writes the stats summary between two APKs to [stream]. + * + * @param itemLimit the max number of items to include in expanded lists + * @param longSummary whether extra details should be included in the summary + * @param itemize whether to expand lists of items + */ + fun writeSummaryTo( + stream: PrintStream, + itemize: Boolean, + longSummary: Boolean, + itemLimit: Int + ) { + fileSizeStats.writeTo(stream, itemize) + if (itemize) stream.println() + + dexStats.writeTo(stream, itemize) + if (itemize) stream.println() + + manifestStats.writeTo(stream, itemize, itemLimit) + if (itemize) stream.println() + + // Resources stats always has an extra blank newline (if there's anything to write) to + // ensure that following lines aren't included as part of the list that's always written for + // resources. + if (resourceStats.writeTo(stream, itemize, itemLimit)) { + stream.println() + } + + assetStats.writeTo(stream, itemize, itemLimit) + if (itemize) stream.println() + + if (longSummary) { + stream.println("*Detailed file differences:*") + if (completeFileDiff != null) { + completeFileDiff.forEach(stream::println) + } else stream.println("APK doesn't exist.") + stream.println() + } + } + } + + /** + * File size stats between two APKs. + * + * @property fileSize the difference in file size between the two APKs + * @property downloadSize the difference in estimated download size between the two APKs. Note + * that the download size for an AAB is likely the base APK download size + the matching + * configuration split APK download size (though this is again an estimate and may vary). + */ + data class FileSizeStats(val fileSize: DiffLong, val downloadSize: DiffLong) { + /** + * Writes the file stats summary between two APKs to [stream]. + * + * @param itemize whether to expand lists of items + */ + fun writeTo(stream: PrintStream, itemize: Boolean) { + fileSize.writeBytesTo(stream, "APK file size") + if (itemize) { + stream.println() + } + downloadSize.writeBytesTo(stream, "APK download size (estimated)") + } + } + + /** + * Dex stats between two APKs. + * + * @property methodCount the difference in total dex methods between the two APks. These + * correspond to declared methods in Kotlin that are being compiled and included in the APK's + * dex files. + */ + data class DexStats(val methodCount: DiffLong) { + /** + * Writes the dex stats summary between two APKs to [stream]. + * + * @param itemize whether to expand lists of items + */ + fun writeTo(stream: PrintStream, itemize: Boolean) { + if (itemize || methodCount.hasDifference()) { + methodCount.writeCountTo(stream, "Method count") + } + } + } + + /** + * Manifest stats between two APks. + * + * @property features the difference list of features between the two APks. These correspond to + * properties that affect who has access to install the app from the Play Store. + * @property permissions the difference list of permissions required between the two APKs. This + * will include both permissions automatically granted by the system and those that require + * user consent on L+ devices. + */ + data class ManifestStats( + val features: DiffList, + val permissions: DiffList + ) { + /** + * Writes the manifest stats summary between two APKs to [stream]. + * + * @param itemLimit the max number of items to include in expanded lists + * @param itemize whether to expand lists of items + */ + fun writeTo(stream: PrintStream, itemize: Boolean, itemLimit: Int) { + if (itemize || features.hasDifference()) { + features.writeTo(stream, "Features", itemize, itemLimit) + if (itemize) { + stream.println() + } + } + if (itemize || permissions.hasDifference()) { + permissions.writeTo(stream, "Permissions", itemize, itemLimit) + } + } + } + + /** + * Resource stats between two APKs. Note that resources include anything defined by Android as a + * resource, and may not necessarily correspond to specific files (e.g. dimensions and strings are + * considered separate resources despite generally being grouped in a few files). + * + * @property resources map from resource type to difference lists of the resource names compared + * between the two APKs. Note that if one APK is missing a certain resource type, the other + * APK will have an empty list within its [DiffList]. + */ + data class ResourceStats( + val resources: Map> + ) { + /** + * Writes the resource stats summary between two APKs to [stream]. + * + * @param itemLimit the max number of items to include in expanded lists + * @param itemize whether to expand lists of items + * @return whether anything was printed + */ + fun writeTo(stream: PrintStream, itemize: Boolean, itemLimit: Int): Boolean { + val totalOldCount = resources.values.sumOf { it.oldCount } + val totalNewCount = resources.values.sumOf { it.newCount } + val totalDifference = totalNewCount - totalOldCount + if (itemize || totalDifference != 0) { + stream.println( + "Resources: $totalOldCount (old), $totalNewCount (new)," + + " **${totalDifference.absoluteValue}** (${totalDifference.convertToDiffString()})" + ) + resources.forEach { (typeName, resourcesList) -> + if (itemize || resourcesList.hasDifference()) { + stream.print("- ") + resourcesList.writeTo( + stream, + linePrefix = typeName.replaceFirstChar { + if (it.isLowerCase()) it.titlecase(Locale.US) else it.toString() + }, + itemize, itemLimit, listIndentation = 2 + ) + } + } + return true + } + return false + } + } + + /** + * Asset stats between two APKs. Note that for reporting simplicity, only immediate assets are + * considered (i.e. those that are direct children of the assets/ directory). + * + * @property assets the difference list between the assets from both APKs + */ + data class AssetStats(val assets: DiffList) { + /** + * Writes the asset stats summary between two APKs to [stream]. + * + * @param itemLimit the max number of items to include in expanded lists + * @param itemize whether to expand lists of items + */ + fun writeTo(stream: PrintStream, itemize: Boolean, itemLimit: Int) { + if (itemize || assets.hasDifference()) { + assets.writeTo(stream, "Lesson assets", itemize, itemLimit) + } + } + } + + /** + * A difference between two long values. + * + * @property oldValue the [Long] value corresponding to the build without changes + * @property newValue the [Long] value corresponding to the build with changes + */ + data class DiffLong(val oldValue: Long, val newValue: Long) { + /** The difference between the two values. */ + val difference: Long by lazy { newValue - oldValue } + + /** Returns whether the two values represented by this difference are actually different. */ + fun hasDifference(): Boolean = difference != 0L + } + + /** + * A difference between two [List]s. + * + * Note that rather than storing a list of [T] values, this stores a list of [DiffEntry]s to + * catalog similarities between the two lists. Note that this list's order is not guaranteed + * relative to the orders of either [oldList] or [newList]. + * + * Finally, this list may be used in cases when the values can guarantee equivalence (such a + * strings), but may not represent cases where those values correspond to files or properties + * whose values have changed (such as string resources). + * + * @property oldList the [List] of values corresponding to the build without changes + * @property newList the [List] of values corresponding to the build with changes + */ + class DiffList( + private val oldList: List, + private val newList: List + ) : AbstractList>() { + private val oldSet by lazy { oldList.toSet() } + private val newSet by lazy { newList.toSet() } + private val combined by lazy { oldSet + newSet } + private val processedEntries by lazy { processDiffs() } + + /** The number of tracked elements corresponding to the build without changes. */ + val oldCount: Int by lazy { oldSet.size } + + /** The number of tracked elements corresponding to the build with changes. */ + val newCount: Int by lazy { newSet.size } + + /** The difference in element count between the two tracked lists. */ + val countDifference: Int by lazy { newCount - oldCount } + + /** Returns whether the two tracked lists have different counts. */ + fun hasDifference(): Boolean = countDifference != 0 + + override val size: Int + get() = processedEntries.size + + override fun get(index: Int): DiffEntry = processedEntries[index] + + private fun processDiffs(): List> { + return combined.map { consideredValue -> + val inOldList = consideredValue in oldSet + val inNewList = consideredValue in newSet + val diffType = if (!inOldList && inNewList) { + DiffType.NEW_ENTRY + } else if (inOldList && !inNewList) { + DiffType.REMOVED_ENTRY + } else DiffType.SAME_ENTRY + return@map DiffEntry(diffType, consideredValue) + } + } + + /** + * Represents a difference between an old & new element in [DiffList]. + * + * @property humanReadableName the human-readable name corresponding to this type + */ + enum class DiffType(val humanReadableName: String) { + /** Represents two entries that are present in both lists. */ + SAME_ENTRY("same"), + + /** Represents an entry only present in the new list. */ + NEW_ENTRY("added"), + + /** Represents an entry only present in the old list. */ + REMOVED_ENTRY("removed") + } + + /** + * Corresponds to an entry within [DiffList]. + * + * @property type the [DiffType] corresponding to this value + * @property value the value that's present in one or both lists (depending on [type]) + */ + data class DiffEntry(val type: DiffType, val value: T) + } + + private companion object { + private fun File.newDirectory(name: String): File { + return File(this, name).also { it.mkdir() } + } + + private fun combineMaps( + oldMap: Map, + newMap: Map, + combineValue: (IV?, IV?) -> OV + ): Map { + val allKeys = oldMap.keys + newMap.keys + return allKeys.map { key -> + return@map key to combineValue(oldMap[key], newMap[key]) + }.toMap() + } + + private fun extractPermissions(permissionDump: List): List { + return permissionDump.filter { line -> + "name=" in line + }.map { line -> + line.substringAfter("name='").substringBefore('\'') + } + } + + private fun combineResourceMaps( + oldResourceMap: Map>, + newResourceMap: Map> + ): Map> { + return combineMaps(oldResourceMap, newResourceMap) { oldResources, newResources -> + DiffList(oldResources ?: listOf(), newResources ?: listOf()) + } + } + + private fun extractResources(resourceDump: List): Map> { + val resourceMap = mutableMapOf>() + lateinit var currentResourceList: MutableList + for (line in resourceDump) { + val tokenizer = StringTokenizer(line) + if (!tokenizer.hasMoreTokens()) continue + when (tokenizer.nextToken()) { + "type" -> { + val typeName = tokenizer.nextToken() + currentResourceList = mutableListOf() + resourceMap[typeName] = currentResourceList + } + "resource" -> { + tokenizer.nextToken() // Skip the ID. + val resourceName = tokenizer.nextToken() + currentResourceList.add(resourceName) + } + // Otherwise, skip it since it's details about the previous resource or top-level + // information. + } + } + return resourceMap + } + + private fun File.extractAssetFileNamesFromApk(): List { + return ZipFile(this).use { zipFile -> + zipFile.entries().asSequence().filter { entry -> + "assets/" in entry.name && "/" !in entry.name.substringAfter("assets/") + }.map { it.name.substringAfter("assets/") }.toList() + } + } + + private fun DiffList.writeTo( + stream: PrintStream, + linePrefix: String, + itemize: Boolean, + itemLimit: Int, + listIndentation: Int = 0 + ) { + val indent = " ".repeat(listIndentation) + stream.print( + "$linePrefix: $oldCount (old), $newCount (new)," + + " **${countDifference.absoluteValue}** (${countDifference.convertToDiffString()})" + ) + if (itemize && hasDifference()) { + stream.println(":") + val newOldAssets = filter { it.type != DiffList.DiffType.SAME_ENTRY } + newOldAssets.take(itemLimit).forEach { (type, assetName) -> + stream.println("$indent- $assetName (${type.humanReadableName})") + } + if (newOldAssets.size > itemLimit) { + val remaining = newOldAssets.size - itemLimit + stream.println("$indent- And $remaining other${if (remaining > 1) "s" else ""}") + } + } else stream.println() + } + + private fun DiffLong.writeCountTo(stream: PrintStream, linePrefix: String) { + stream.println( + "$linePrefix: $oldValue (old), $newValue (new)," + + " **${difference.absoluteValue}** (${difference.convertToDiffString()})" + ) + } + + private fun DiffLong.writeBytesTo(stream: PrintStream, linePrefix: String) { + stream.println( + "$linePrefix: ${oldValue.formatAsBytes()} (old), ${newValue.formatAsBytes()} (new)," + + " **${difference.formatAsBytes()}** (${difference.convertToDiffString()})" + ) + } + + private fun Long.formatAsBytes(): String { + val magnitude = absoluteValue + return when { + magnitude < 10_000L -> "$magnitude bytes" + magnitude < 10_000_000L -> "${magnitude / 1024} KiB" + magnitude < 10_000_000_000L -> "${magnitude / (1024 * 1024)} MiB" + else -> "${magnitude / (1024 * 1024 * 1024)} GiB" + } + } + + private fun Int.convertToDiffString() = when { + this > 0 -> "Added" + this < 0 -> "Removed" + else -> "No change" + } + + private fun Long.convertToDiffString() = when { + this > 0 -> "Added" + this < 0 -> "Removed" + else -> "No change" + } + } +} diff --git a/scripts/src/java/org/oppia/android/scripts/common/AndroidBuildSdkProperties.kt b/scripts/src/java/org/oppia/android/scripts/common/AndroidBuildSdkProperties.kt new file mode 100644 index 00000000000..a85a11bfdcc --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/common/AndroidBuildSdkProperties.kt @@ -0,0 +1,32 @@ +package org.oppia.android.scripts.common + +import java.io.InputStream +import java.util.Properties + +/** Represents Android SDK properties available at build time. */ +class AndroidBuildSdkProperties { + private val androidSdkProperties by lazy { + Properties().also { props -> + AndroidBuildSdkProperties::class.java.loadResource(SDK_INFO_PROPERTIES_PATH).use(props::load) + } + } + + /** The version of the Android SDK used for building. */ + val buildSdkVersion: Int + get() = androidSdkProperties.getExpectedProperty("build_sdk_version").toInt() + + /** The version of Android CLI tools used for building. */ + val buildToolsVersion: String + get() = androidSdkProperties.getExpectedProperty("build_tools_version") + + private companion object { + private const val SDK_INFO_PROPERTIES_PATH = "sdk_info.properties" + + private fun Class<*>.loadResource(name: String): InputStream = + checkNotNull(getResourceAsStream(name)) { "Failed to find resource: $name." } + + private fun Properties.getExpectedProperty(key: String): String { + return checkNotNull(getProperty(key)) { "Expected property to be present: $key" } + } + } +} diff --git a/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel b/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel index b9f8c452efb..b4942de8721 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel +++ b/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel @@ -4,6 +4,18 @@ generic operations. """ load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") +load("//:build_vars.bzl", "BUILD_SDK_VERSION", "BUILD_TOOLS_VERSION") + +kt_jvm_library( + name = "android_build_sdk_properties", + testonly = True, + srcs = [ + "AndroidBuildSdkProperties.kt", + ], + resource_strip_prefix = "scripts/src/java", + resources = [":_sdk_info_properties"], + visibility = ["//scripts:oppia_script_library_visibility"], +) kt_jvm_library( name = "bazel_client", @@ -88,3 +100,13 @@ kt_jvm_library( "//third_party:org_jetbrains_kotlinx_kotlinx-coroutines-core", ], ) + +genrule( + name = "_sdk_info_properties", + srcs = [], + outs = ["sdk_info.properties"], + cmd = """ + echo build_sdk_version={0} > \"$@\" + echo build_tools_version={1} >> \"$@\" + """.format(BUILD_SDK_VERSION, BUILD_TOOLS_VERSION), +) diff --git a/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt b/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt index 01d59a6f505..9aede2fd16f 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt +++ b/scripts/src/java/org/oppia/android/scripts/common/BazelClient.kt @@ -84,7 +84,7 @@ class BazelClient(private val rootDirectory: File, private val commandExecutor: } /** - * Returns the list of direct and indirect maven third-party dependencies on which the specified + * Returns the list of direct and indirect Maven third-party dependencies on which the specified * binary depends. */ fun retrieveThirdPartyMavenDepsListForBinary(binaryTarget: String): List { diff --git a/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesListCheck.kt b/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesListCheck.kt index 4db0c1d7f45..d6b5ff1d985 100644 --- a/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesListCheck.kt +++ b/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesListCheck.kt @@ -8,7 +8,7 @@ import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher import org.oppia.android.scripts.proto.MavenDependency /** - * The main entrypoint for verifying the list of third-party maven dependencies in + * The main entrypoint for verifying the list of third-party Maven dependencies in * maven_dependencies.textproto is up-to-date. * * Usage: @@ -18,7 +18,7 @@ import org.oppia.android.scripts.proto.MavenDependency * * Arguments: * - path_to_root: directory path to the root of the Oppia Android repository. - * - path_to_maven_install_json: relative path to the maven_install.json file. + * - path_to_maven_install_json: relative path to the Maven installation manifest file. * - path_to_maven_dependencies_pb: relative path to the maven_dependencies.pb file. * * Example: @@ -41,7 +41,7 @@ class MavenDependenciesListCheck( private val commandExecutor: CommandExecutor = CommandExecutorImpl(scriptBgDispatcher) ) { /** - * Verifies that the list of third-party maven dependencies in maven_dependnecies.textproto is + * Verifies that the list of third-party Maven dependencies in maven_dependnecies.textproto is * up-to-date. */ fun main(args: Array) { diff --git a/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesRetriever.kt b/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesRetriever.kt index 78f2e20dda7..43f0193047c 100644 --- a/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesRetriever.kt +++ b/scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesRetriever.kt @@ -24,7 +24,7 @@ import javax.xml.parsers.DocumentBuilderFactory private const val MAVEN_PREFIX = "@maven//:" -/** Helper to compile the third-party maven dependencies list for Oppia Android. */ +/** Helper to compile the third-party Maven dependencies list for Oppia Android. */ class MavenDependenciesRetriever( private val rootPath: String, private val mavenArtifactPropertyFetcher: MavenArtifactPropertyFetcher, @@ -167,7 +167,7 @@ class MavenDependenciesRetriever( } /** - * Retrieves the list of maven dependencies from maven_dependencies.textproto. + * Retrieves the list of Maven dependencies from maven_dependencies.textproto. * * @param pathToPbFile path to the pb file to be parsed * @return list of dependencies @@ -184,7 +184,7 @@ class MavenDependenciesRetriever( * files. * * @param finalDependenciesList list of dependencies that is obtained by the intersection of - * the list generated by Bazel Query and the list generated from maven_install.json + * the list generated by Bazel Query and the list generated from the Maven install manifest * @return a [Deferred] of a list of [MavenListDependency] that has dependencies with licenses * extracted from their POM files */ @@ -233,9 +233,9 @@ class MavenDependenciesRetriever( } /** - * Parses the maven_install.json file to compile the list of maven dependencies. + * Parses the Maven install manifest file to compile the list of Maven dependencies. * - * @param pathToMavenInstall path to the maven_install.json file + * @param pathToMavenInstall path to the Maven install manifest file * @param bazelQueryDepsNames list of dependency names obtained from the bazel query * @return a [Deferred] of a list of [MavenListDependency]s that contains the artifact name and a * URL that is used to obtain the URL of the POM file of the dependency diff --git a/scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt b/scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt index 3d1292b94d1..f58b82dac66 100644 --- a/scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt +++ b/scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt @@ -20,7 +20,7 @@ import org.oppia.android.scripts.proto.MavenDependencyList * * Arguments: * - path_to_directory_root: directory path to the root of the Oppia Android repository. - * - path_to_maven_install_json: relative path to the maven_install.json file. + * - path_to_maven_install_json: relative path to the Maven install manifest file. * - path_to_maven_dependencies_textproto: relative path to the maven_dependencies.textproto * - path_to_maven_dependencies_pb: relative path to the maven_dependencies.pb file. * Example: @@ -41,7 +41,7 @@ class GenerateMavenDependenciesList( private val commandExecutor: CommandExecutor = CommandExecutorImpl(scriptBgDispatcher) ) { /** - * Compiles a list of third-party maven dependencies along with their license links on + * Compiles a list of third-party Maven dependencies along with their license links on * which Oppia Android depends and write them in maven_dependencies.textproto. */ fun main(args: Array) { diff --git a/scripts/src/java/org/oppia/android/scripts/maven/RetrieveLicenseTexts.kt b/scripts/src/java/org/oppia/android/scripts/maven/RetrieveLicenseTexts.kt index c7ce282c995..174980cb0cd 100644 --- a/scripts/src/java/org/oppia/android/scripts/maven/RetrieveLicenseTexts.kt +++ b/scripts/src/java/org/oppia/android/scripts/maven/RetrieveLicenseTexts.kt @@ -93,7 +93,7 @@ class RetrieveLicenseTexts( } /** - * Retrieve the list of maven dependencies from maven_dependencies.textproto. + * Retrieve the list of Maven dependencies from maven_dependencies.textproto. * * @param pathToPbFile path to the pb file to be parsed * @return list of [MavenDependency]s diff --git a/scripts/src/java/org/oppia/android/scripts/maven/model/BUILD.bazel b/scripts/src/java/org/oppia/android/scripts/maven/model/BUILD.bazel index 9e0180a3807..4ad35ca52ee 100644 --- a/scripts/src/java/org/oppia/android/scripts/maven/model/BUILD.bazel +++ b/scripts/src/java/org/oppia/android/scripts/maven/model/BUILD.bazel @@ -1,5 +1,5 @@ """ -Data structures corresponding to parsing maven_install.json. +Data structures corresponding to parsing Maven installation manifests. """ load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") diff --git a/scripts/src/javatests/org/oppia/android/scripts/apkstats/Aapt2ClientTest.kt b/scripts/src/javatests/org/oppia/android/scripts/apkstats/Aapt2ClientTest.kt new file mode 100644 index 00000000000..bb19478ac27 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/apkstats/Aapt2ClientTest.kt @@ -0,0 +1,62 @@ +package org.oppia.android.scripts.apkstats + +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.oppia.android.scripts.common.AndroidBuildSdkProperties +import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher +import org.oppia.android.testing.assertThrows +import java.util.concurrent.TimeUnit + +/** + * Tests for [Aapt2Client]. + * + * Note that this test executes real commands on the local filesystem. + */ +// Same parameter value: helpers reduce test context, even if they are used by 1 test. +// Function name: test names are conventionally named with underscores. +@Suppress("SameParameterValue", "FunctionName") +class Aapt2ClientTest { + @field:[Rule JvmField] var tempFolder = TemporaryFolder() + + private val sdkProperties = AndroidBuildSdkProperties() + + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } + private val commandExecutor by lazy { initializeCommandExecutorWithLongProcessWaitTime() } + + // TODO(#4971): Finish the tests for this suite. + + @After + fun tearDown() { + scriptBgDispatcher.close() + } + + @Test + fun testDumpResources_forNonExistentApk_throwsException() { + val aapt2Client = createAapt2Client() + + val exception = assertThrows() { + aapt2Client.dumpResources("fake_file.apk") + } + + assertThat(exception).hasMessageThat().contains("No such file or directory") + } + + private fun createAapt2Client(): Aapt2Client { + return Aapt2Client( + tempFolder.root.absolutePath, + sdkProperties.buildToolsVersion, + scriptBgDispatcher, + commandExecutor + ) + } + + private fun initializeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { + return CommandExecutorImpl( + scriptBgDispatcher, processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES + ) + } +} diff --git a/scripts/src/javatests/org/oppia/android/scripts/apkstats/ApkAnalyzerClientTest.kt b/scripts/src/javatests/org/oppia/android/scripts/apkstats/ApkAnalyzerClientTest.kt new file mode 100644 index 00000000000..4df1efba563 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/apkstats/ApkAnalyzerClientTest.kt @@ -0,0 +1,65 @@ +package org.oppia.android.scripts.apkstats + +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.oppia.android.scripts.common.AndroidBuildSdkProperties +import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher +import org.oppia.android.testing.assertThrows +import java.lang.IllegalArgumentException +import java.util.concurrent.TimeUnit + +/** + * Tests for [ApkAnalyzerClient]. + * + * Note that this test executes real commands on the local filesystem. + */ +// Same parameter value: helpers reduce test context, even if they are used by 1 test. +// Function name: test names are conventionally named with underscores. +@Suppress("SameParameterValue", "FunctionName") +class ApkAnalyzerClientTest { + @field:[Rule JvmField] var tempFolder = TemporaryFolder() + + private val sdkProperties = AndroidBuildSdkProperties() + + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } + private val commandExecutor by lazy { initializeCommandExecutorWithLongProcessWaitTime() } + + // TODO(#4971): Finish the tests for this suite. + + @After + fun tearDown() { + scriptBgDispatcher.close() + } + + @Test + fun testComputeDownloadSize_forNonExistentApk_throwsException() { + val apkAnalyzerClient = createApkAnalyzerClient() + + val exception = assertThrows() { + apkAnalyzerClient.computeDownloadSize("fake.apk") + } + + assertThat(exception).hasMessageThat().contains("Cannot open apk") + } + + private fun createApkAnalyzerClient(): ApkAnalyzerClient { + return ApkAnalyzerClient( + Aapt2Client( + tempFolder.root.absolutePath, + sdkProperties.buildToolsVersion, + scriptBgDispatcher, + commandExecutor + ) + ) + } + + private fun initializeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { + return CommandExecutorImpl( + scriptBgDispatcher, processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES + ) + } +} diff --git a/scripts/src/javatests/org/oppia/android/scripts/apkstats/BUILD.bazel b/scripts/src/javatests/org/oppia/android/scripts/apkstats/BUILD.bazel new file mode 100644 index 00000000000..78073d09666 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/apkstats/BUILD.bazel @@ -0,0 +1,56 @@ +""" +Tests corresponding to libraries that help with computing APK & AAB stats. +""" + +load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_test") + +kt_jvm_test( + name = "Aapt2ClientTest", + srcs = ["Aapt2ClientTest.kt"], + data = ["@androidsdk//:aapt2_binary"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/apkstats:aapt2_client", + "//scripts/src/java/org/oppia/android/scripts/common:android_build_sdk_properties", + "//testing:assertion_helpers", + "//third_party:com_google_truth_truth", + ], +) + +kt_jvm_test( + name = "ApkAnalyzerClientTest", + srcs = ["ApkAnalyzerClientTest.kt"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/apkstats:apk_analyzer_client", + "//scripts/src/java/org/oppia/android/scripts/common:android_build_sdk_properties", + "//testing:assertion_helpers", + "//third_party:com_google_truth_truth", + ], +) + +kt_jvm_test( + name = "BundleToolClientTest", + srcs = ["BundleToolClientTest.kt"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/apkstats:bundle_tool_client", + "//testing:assertion_helpers", + "//third_party:com_google_truth_truth", + ], +) + +kt_jvm_test( + name = "ComputeAabDifferencesTest", + srcs = ["ComputeAabDifferencesTest.kt"], + data = ["@androidsdk//:aapt2_binary"], + runtime_deps = [ + "//third_party:android_bundletool", + ], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/apkstats:aapt2_client", + "//scripts/src/java/org/oppia/android/scripts/apkstats:apk_analyzer_client", + "//scripts/src/java/org/oppia/android/scripts/apkstats:bundle_tool_client", + "//scripts/src/java/org/oppia/android/scripts/apkstats:compute_aab_differences_lib", + "//scripts/src/java/org/oppia/android/scripts/common:android_build_sdk_properties", + "//testing:assertion_helpers", + "//third_party:com_google_truth_truth", + ], +) diff --git a/scripts/src/javatests/org/oppia/android/scripts/apkstats/BundleToolClientTest.kt b/scripts/src/javatests/org/oppia/android/scripts/apkstats/BundleToolClientTest.kt new file mode 100644 index 00000000000..3ddf028ff51 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/apkstats/BundleToolClientTest.kt @@ -0,0 +1,51 @@ +package org.oppia.android.scripts.apkstats + +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.oppia.android.scripts.common.CommandExecutorImpl +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher +import org.oppia.android.testing.assertThrows +import java.util.concurrent.TimeUnit + +/** + * Tests for [BundleToolClient]. + * + * Note that this test executes real commands on the local filesystem. + */ +// Same parameter value: helpers reduce test context, even if they are used by 1 test. +// Function name: test names are conventionally named with underscores. +@Suppress("SameParameterValue", "FunctionName") +class BundleToolClientTest { + @field:[Rule JvmField] var tempFolder = TemporaryFolder() + + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } + private val commandExecutor by lazy { initializeCommandExecutorWithLongProcessWaitTime() } + + // TODO(#4971): Finish the tests for this suite. + + @After + fun tearDown() { + scriptBgDispatcher.close() + } + + @Test + fun testBuildUniversalApk_forNonExistentAab_throwsException() { + val bundleToolClient = + BundleToolClient(tempFolder.root.absolutePath, scriptBgDispatcher, commandExecutor) + + val exception = assertThrows() { + bundleToolClient.buildUniversalApk("fake.aab", "fake.apk") + } + + assertThat(exception).hasMessageThat().contains("was not found") + } + + private fun initializeCommandExecutorWithLongProcessWaitTime(): CommandExecutorImpl { + return CommandExecutorImpl( + scriptBgDispatcher, processTimeout = 5, processTimeoutUnit = TimeUnit.MINUTES + ) + } +} diff --git a/scripts/src/javatests/org/oppia/android/scripts/apkstats/ComputeAabDifferencesTest.kt b/scripts/src/javatests/org/oppia/android/scripts/apkstats/ComputeAabDifferencesTest.kt new file mode 100644 index 00000000000..b08753158d7 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/apkstats/ComputeAabDifferencesTest.kt @@ -0,0 +1,69 @@ +package org.oppia.android.scripts.apkstats + +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.oppia.android.scripts.common.AndroidBuildSdkProperties +import org.oppia.android.scripts.common.ScriptBackgroundCoroutineDispatcher +import org.oppia.android.testing.assertThrows +import java.lang.IllegalStateException + +/** + * Tests for [ComputeAabDifferences]. + * + * Note that this test executes real commands on the local filesystem. + */ +// Same parameter value: helpers reduce test context, even if they are used by 1 test. +// Function name: test names are conventionally named with underscores. +@Suppress("SameParameterValue", "FunctionName") +class ComputeAabDifferencesTest { + @field:[Rule JvmField] var tempFolder = TemporaryFolder() + + private val scriptBgDispatcher by lazy { ScriptBackgroundCoroutineDispatcher() } + + // TODO(#4971): Finish the tests for this suite. + + @After + fun tearDown() { + scriptBgDispatcher.close() + } + + @Test + fun testComputeBuildStats_forZeroProfiles_returnsEmptyStats() { + val differencesUtility = createComputeAabDifferences() + + val stats = differencesUtility.computeBuildStats() + + assertThat(stats.aabStats).isEmpty() + } + + @Test + fun testComputeBuildStats_forProfileWithMissingFiles_throwsException() { + val differencesUtility = createComputeAabDifferences() + val profile = createProfile(oldAabFilePath = "fake.apk", newAabFilePath = "fake.apk") + + val exception = assertThrows() { + differencesUtility.computeBuildStats(profile) + } + + assertThat(exception).hasMessageThat().contains("was not found") + } + + private fun createComputeAabDifferences(): ComputeAabDifferences { + return ComputeAabDifferences( + workingDirectoryPath = tempFolder.root.absoluteFile.normalize().path, + sdkProperties = AndroidBuildSdkProperties(), + scriptBgDispatcher + ) + } + + private fun createProfile( + oldAabFilePath: String, + newAabFilePath: String, + buildFlavor: String = "dev" + ): ComputeAabDifferences.AabProfile { + return ComputeAabDifferences.AabProfile(buildFlavor, oldAabFilePath, newAabFilePath) + } +} diff --git a/scripts/src/javatests/org/oppia/android/scripts/common/AndroidBuildSdkPropertiesTest.kt b/scripts/src/javatests/org/oppia/android/scripts/common/AndroidBuildSdkPropertiesTest.kt new file mode 100644 index 00000000000..d7cb9a5ebe9 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/common/AndroidBuildSdkPropertiesTest.kt @@ -0,0 +1,31 @@ +package org.oppia.android.scripts.common + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +/** + * Tests for [AndroidBuildSdkProperties]. + * + * Note that these tests have a few caveats: + * 1. They are a bit fragile since they're directly testing the local Bazel configuration. As the + * team changes its dependency versions in the future, this suite will need to be updated. + * 2. There's no way to test the "failing state" since the build graph guarantees that the SDK info + * file is included at runtime. The implementation makes this assumption, as well. + */ +// Function name: test names are conventionally named with underscores. +@Suppress("FunctionName") +class AndroidBuildSdkPropertiesTest { + @Test + fun testBuildSdkVersion_isTheCorrectSdkVersion() { + val properties = AndroidBuildSdkProperties() + + assertThat(properties.buildSdkVersion).isEqualTo(33) + } + + @Test + fun testBuildToolsVersion_isTheCorrectVersion() { + val properties = AndroidBuildSdkProperties() + + assertThat(properties.buildToolsVersion).isEqualTo("29.0.2") + } +} diff --git a/scripts/src/javatests/org/oppia/android/scripts/common/BUILD.bazel b/scripts/src/javatests/org/oppia/android/scripts/common/BUILD.bazel index d021c5d4495..b4e81b569db 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/common/BUILD.bazel +++ b/scripts/src/javatests/org/oppia/android/scripts/common/BUILD.bazel @@ -19,6 +19,15 @@ kt_jvm_test( ], ) +kt_jvm_test( + name = "AndroidBuildSdkPropertiesTest", + srcs = ["AndroidBuildSdkPropertiesTest.kt"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/common:android_build_sdk_properties", + "//third_party:com_google_truth_truth", + ], +) + kt_jvm_test( name = "CommandExecutorImplTest", srcs = ["CommandExecutorImplTest.kt"], diff --git a/scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt b/scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt index 06b290d0e47..ca789a2ada5 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt @@ -1342,7 +1342,7 @@ class GenerateMavenDependenciesListTest { ) } - /** Helper function to write a fake maven_install.json file. */ + /** Helper function to write a fake Maven install manifest file. */ private fun writeMavenInstallJson(file: File) { file.writeText( """ diff --git a/third_party/BUILD.bazel b/third_party/BUILD.bazel index f45d5300cd6..74f84964b65 100644 --- a/third_party/BUILD.bazel +++ b/third_party/BUILD.bazel @@ -65,6 +65,14 @@ android_library( ], ) +android_library( + name = "com_google_archivepatcher", + visibility = ["//visibility:public"], + exports = [ + "@archive_patcher//tools", + ], +) + java_library( name = "moshi", exported_plugins = ["//tools:moshi_annotation_processor_plugin"], @@ -132,6 +140,19 @@ kt_jvm_library( exports = [":_kotlinx-coroutines-core-jvm_import_do_not_depend"], ) +java_library( + name = "com_google_guava_guava_jre", + visibility = ["//scripts:__subpackages__"], + exports = [ + "//third_party:com_google_errorprone_error_prone_annotations", + "//third_party:com_google_guava_failureaccess", + "//third_party:com_google_j2objc_j2objc-annotations", + "//third_party:org_checkerframework_checker-compat-qual", + "//third_party:org_checkerframework_checker-qual", + "@guava_jre//jar", + ], +) + # This solution is based on https://github.com/Bencodes/bazel_issue_13553 for Bazel issue: # https://github.com/bazelbuild/bazel/issues/13553. genrule( @@ -162,14 +183,17 @@ java_library( name = "glide_compiler", exported_plugins = ["//tools:glide_compiler_plugin"], visibility = ["//visibility:public"], - exports = [ - "//third_party:com_github_bumptech_glide_compiler", - ], ) -java_binary( +android_library( name = "android_bundletool", + visibility = ["//visibility:public"], + exports = ["@android_bundletool//jar"], +) + +java_binary( + name = "android_bundletool_binary", main_class = "com.android.tools.build.bundletool.BundleToolMain", visibility = ["//visibility:public"], - runtime_deps = ["@android_bundletool//jar"], + runtime_deps = [":android_bundletool"], ) diff --git a/third_party/maven_install.json b/third_party/maven_install.json index 6af4aaf5b4c..f25b55f9094 100644 --- a/third_party/maven_install.json +++ b/third_party/maven_install.json @@ -1,7 +1,7 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -519121942, - "__RESOLVED_ARTIFACTS_HASH": -749919456, + "__INPUT_ARTIFACTS_HASH": 1828687201, + "__RESOLVED_ARTIFACTS_HASH": 1257218600, "conflict_resolution": { "androidx.constraintlayout:constraintlayout:1.1.3": "androidx.constraintlayout:constraintlayout:2.0.1", "androidx.core:core:1.0.1": "androidx.core:core:1.3.1", @@ -514,6 +514,30 @@ }, "version": "3.4.2" }, + "com.android.tools.analytics-library:protos": { + "shasums": { + "jar": "c1f0270c758f61db311d1365af2eaab46318c0ecde1d15aed6c20d5b32ed3ade" + }, + "version": "30.0.4" + }, + "com.android.tools.analytics-library:shared": { + "shasums": { + "jar": "91cada37f5035b5d313cce64fde2a3c3a3f58b267edb80b0e3e94372e3bcf2d1" + }, + "version": "30.0.4" + }, + "com.android.tools.apkparser:apkanalyzer": { + "shasums": { + "jar": "be0f9e079fe166da36127b780de9ffcb297b511b642cc09c0dff75a2ee47c454" + }, + "version": "30.0.4" + }, + "com.android.tools.apkparser:binary-resources": { + "shasums": { + "jar": "c8fb8f656fe329375d4c669fc00bd396e6debd098a1304ab0189e54e6ab3f303" + }, + "version": "30.0.4" + }, "com.android.tools.build.jetifier:jetifier-core": { "shasums": { "jar": "c9f8b016144cfb31c5aee92d47f34de23289167cac5e8ef68365b2dd05766f11" @@ -526,11 +550,113 @@ }, "version": "7.3.1-8691043" }, + "com.android.tools.build:builder-model": { + "shasums": { + "jar": "6ab2f2371f2bcd48a3a623613d0bc00838b5bfb4e6c4e49e324ebcf88f7feb0e" + }, + "version": "7.0.4" + }, + "com.android.tools.build:builder-test-api": { + "shasums": { + "jar": "cfcf933229e368231a96b79b1ec394d7cbcf822eaf8a3ba44c92ba52d959c90e" + }, + "version": "7.0.4" + }, + "com.android.tools.build:manifest-merger": { + "shasums": { + "jar": "44339ed30995742f96147891356135210599c96a0a00a62d81465ce2b8ec1f98" + }, + "version": "30.0.4" + }, + "com.android.tools.ddms:ddmlib": { + "shasums": { + "jar": "7f706a995f5b3047d80e28b4edb584b2338b7b47b9629ed1607011f39c17e14c" + }, + "version": "30.0.4" + }, + "com.android.tools.external.com-intellij:intellij-core": { + "shasums": { + "jar": "e5577bfe5be2937212012cda8ead120fc2dd96a3427533b4b4aed2c60fe3e8c4" + }, + "version": "30.0.4" + }, + "com.android.tools.external.com-intellij:kotlin-compiler": { + "shasums": { + "jar": "5e2456b33e7879442d10c666c2eae756c3e711d087f7b346b1f2035f0c3fc18d" + }, + "version": "30.0.4" + }, + "com.android.tools.external.org-jetbrains:uast": { + "shasums": { + "jar": "72be32fa6463c307ce52abef1317b73bb515e7ff68475ad555cad37b84c1563d" + }, + "version": "30.0.4" + }, + "com.android.tools.layoutlib:layoutlib-api": { + "shasums": { + "jar": "9ba23153dd4aa795313d8b908cd0acd553ec5e2d7b4a5c5944609b6a47a768d4" + }, + "version": "30.0.4" + }, + "com.android.tools.lint:lint-api": { + "shasums": { + "jar": "ebbdcbed4149b8a781262473feaa113166750ca3042735fd9fcd79f8e9103037" + }, + "version": "30.0.4" + }, + "com.android.tools.lint:lint-checks": { + "shasums": { + "jar": "7bd817ff4060fdb13750c5a45daccd768b47a1363607da3ee9eceba46c99d569" + }, + "version": "30.0.4" + }, + "com.android.tools.lint:lint-model": { + "shasums": { + "jar": "fb7561562b7d3d3edf015c12367f267804a9824083a9e10944daf71473ce9bc5" + }, + "version": "30.0.4" + }, "com.android.tools:annotations": { "shasums": { - "jar": "1877849936f48cd65857bf448743f9a0bf93ed47fe57527d9ad8af875024888d" + "jar": "df132b4daa2bb73770cd459c4c6b9b927695dd8a89592ab149ecdb7e0d8506ed" + }, + "version": "30.0.4" + }, + "com.android.tools:common": { + "shasums": { + "jar": "d1aa2b6a43843c385ddd246fb0118acf45afad5317edb3c508a4604a288fa9c9" }, - "version": "26.4.2" + "version": "30.0.4" + }, + "com.android.tools:dvlib": { + "shasums": { + "jar": "61edccccecc1ffca47f442326bd7c1d73f7256ef9adaac834c1d639a142e49b9" + }, + "version": "30.0.4" + }, + "com.android.tools:repository": { + "shasums": { + "jar": "ff77dda7a094a6b2ae5d631ecaca59c26c0673f59b1e5f874352c17e92eb9a1f" + }, + "version": "30.0.4" + }, + "com.android.tools:sdk-common": { + "shasums": { + "jar": "b4617630f9c9cc8ad80f1aab980cc560d8f76bd26e84421ea54f38e036b3be48" + }, + "version": "30.0.4" + }, + "com.android.tools:sdklib": { + "shasums": { + "jar": "bcfe68d2ec02569f40978c9380a3302d60f67772c0dea95838f817f5cf3505a8" + }, + "version": "30.0.4" + }, + "com.beust:jcommander": { + "shasums": { + "jar": "156be736199c990321d9ff77090b199629cfc9865e2d6c13f7cd291bb1641817" + }, + "version": "1.64" }, "com.crashlytics.sdk.android:answers:aar": { "shasums": { @@ -952,6 +1078,12 @@ }, "version": "1.3" }, + "com.google.jimfs:jimfs": { + "shasums": { + "jar": "c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd" + }, + "version": "1.1" + }, "com.google.protobuf:protobuf-java": { "shasums": { "jar": "4ac549b192694141958049f060a1c826a33342f619e108ced8c17d9877f5e3ed" @@ -1072,6 +1204,24 @@ }, "version": "1.10.2" }, + "com.sun.activation:javax.activation": { + "shasums": { + "jar": "993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce" + }, + "version": "1.2.0" + }, + "com.sun.istack:istack-commons-runtime": { + "shasums": { + "jar": "4ffabb06be454a05e4398e20c77fa2b6308d4b88dfbef7ca30a76b5b7d5505ef" + }, + "version": "3.0.8" + }, + "com.sun.xml.fastinfoset:FastInfoset": { + "shasums": { + "jar": "056f3a1e144409f21ed16afc26805f58e9a21f3fce1543c42d400719d250c511" + }, + "version": "1.2.16" + }, "commons-codec:commons-codec": { "shasums": { "jar": "4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569" @@ -1084,6 +1234,12 @@ }, "version": "2.4" }, + "commons-logging:commons-logging": { + "shasums": { + "jar": "daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636" + }, + "version": "1.2" + }, "io.fabric.sdk.android:fabric:aar": { "shasums": { "jar": "36df4b321ec95e31226ff5abaae73e66f3a99dedddbc2d03054c4e141c8aaa5c" @@ -1144,12 +1300,24 @@ }, "version": "0.1.0" }, + "jakarta.activation:jakarta.activation-api": { + "shasums": { + "jar": "8b0a0f52fa8b05c5431921a063ed866efaa41dadf2e3a7ee3e1961f2b0d9645b" + }, + "version": "1.2.1" + }, "jakarta.json:jakarta.json-api": { "shasums": { "jar": "f2472507ad2cc12f2aef08a2f7a628cd1c3f855668a6dcb2aa9a30d9b909b998" }, "version": "2.1.2" }, + "jakarta.xml.bind:jakarta.xml.bind-api": { + "shasums": { + "jar": "69156304079bdeed9fc0ae3b39389f19b3cc4ba4443bc80508995394ead742ea" + }, + "version": "2.3.2" + }, "javax.annotation:javax.annotation-api": { "shasums": { "jar": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b" @@ -1180,6 +1348,18 @@ }, "version": "1.10.20" }, + "net.java.dev.jna:jna": { + "shasums": { + "jar": "5557e235a8aa2f9766d5dc609d67948f2a8832c2d796cea9ef1d6cbe0b3b7eaf" + }, + "version": "5.6.0" + }, + "net.java.dev.jna:jna-platform": { + "shasums": { + "jar": "9ecea8bf2b1b39963939d18b70464eef60c508fed8820f9dcaba0c35518eabf7" + }, + "version": "5.6.0" + }, "net.ltgt.gradle.incap:incap": { "shasums": { "jar": "b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd" @@ -1204,6 +1384,36 @@ }, "version": "4.5.3" }, + "org.apache.commons:commons-compress": { + "shasums": { + "jar": "0aeb625c948c697ea7b205156e112363b59ed5e2551212cd4e460bdb72c7c06e" + }, + "version": "1.20" + }, + "org.apache.httpcomponents:httpclient": { + "shasums": { + "jar": "c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7" + }, + "version": "4.5.6" + }, + "org.apache.httpcomponents:httpcore": { + "shasums": { + "jar": "78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd" + }, + "version": "4.4.10" + }, + "org.apache.httpcomponents:httpmime": { + "shasums": { + "jar": "0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e" + }, + "version": "4.5.6" + }, + "org.bouncycastle:bcpkix-jdk15on": { + "shasums": { + "jar": "7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca" + }, + "version": "1.56" + }, "org.bouncycastle:bcprov-jdk15on": { "shasums": { "jar": "e78f96eb59066c94c94fb2d6b5eb80f52feac6f5f9776898634f8addec6e2137" @@ -1234,6 +1444,18 @@ }, "version": "1.1.2" }, + "org.glassfish.jaxb:jaxb-runtime": { + "shasums": { + "jar": "e6e0a1e89fb6ff786279e6a0082d5cef52dc2ebe67053d041800737652b4fd1b" + }, + "version": "2.3.2" + }, + "org.glassfish.jaxb:txw2": { + "shasums": { + "jar": "4a6a9f483388d461b81aa9a28c685b8b74c0597993bf1884b04eddbca95f48fe" + }, + "version": "2.3.2" + }, "org.hamcrest:hamcrest-core": { "shasums": { "jar": "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9" @@ -1378,6 +1600,12 @@ }, "version": "13.0" }, + "org.jvnet.staxex:stax-ex": { + "shasums": { + "jar": "20522549056e9e50aa35ef0b445a2e47a53d06be0b0a9467d704e2483ffb049a" + }, + "version": "1.8.1" + }, "org.mockito.kotlin:mockito-kotlin": { "shasums": { "jar": "12995c7fdb8de84cf58fca31f1cd8810eeaa39e63c00e61ffcb2b49a4305f21f" @@ -1492,11 +1720,41 @@ }, "version": "4.5" }, + "org.smali:baksmali": { + "shasums": { + "jar": "1990932c4c8cf334a640e52c4bd0426e4abb2c2fc94f1803711ea8460c06de50" + }, + "version": "2.2.4" + }, + "org.smali:dexlib2": { + "shasums": { + "jar": "cb2677bfb66cfbc954e96e806ac6bda13051ad37754f9d1bcce38514e50e41e6" + }, + "version": "2.2.4" + }, + "org.smali:util": { + "shasums": { + "jar": "d7de146d2b6dc9d0b75cb6c7ff17ca68ef24049dc6038b84b5f6b22cd00ddd46" + }, + "version": "2.2.4" + }, "org.snakeyaml:snakeyaml-engine": { "shasums": { "jar": "2652199af40c9aa2f1782400d2dfbbf4e5226208c4e05ddd4059c3d6d9cd1505" }, "version": "2.6" + }, + "xerces:xercesImpl": { + "shasums": { + "jar": "b50d3a4ca502faa4d1c838acb8aa9480446953421f7327e338c5dda3da5e76d0" + }, + "version": "2.12.0" + }, + "xml-apis:xml-apis": { + "shasums": { + "jar": "a840968176645684bb01aed376e067ab39614885f9eee44abe35a5f20ebe7fad" + }, + "version": "1.4.01" } }, "dependencies": { @@ -1884,6 +2142,34 @@ "androidx.room:room-runtime:aar", "androidx.work:work-runtime-ktx:aar" ], + "com.android.tools.analytics-library:protos": [ + "com.google.protobuf:protobuf-java" + ], + "com.android.tools.analytics-library:shared": [ + "com.android.tools.analytics-library:protos", + "com.android.tools:annotations", + "com.android.tools:common", + "com.google.code.gson:gson", + "com.google.guava:guava", + "net.java.dev.jna:jna-platform", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + ], + "com.android.tools.apkparser:apkanalyzer": [ + "com.android.tools.apkparser:binary-resources", + "com.android.tools.lint:lint-api", + "com.android.tools.lint:lint-checks", + "com.android.tools:annotations", + "com.android.tools:common", + "com.android.tools:sdk-common", + "com.android.tools:sdklib", + "org.smali:baksmali", + "org.smali:dexlib2" + ], + "com.android.tools.apkparser:binary-resources": [ + "com.android.tools:annotations", + "com.google.code.findbugs:jsr305", + "com.google.guava:guava" + ], "com.android.tools.build.jetifier:jetifier-core": [ "com.google.code.gson:gson", "org.jetbrains.kotlin:kotlin-stdlib" @@ -1891,6 +2177,130 @@ "com.android.tools.build:aapt2-proto": [ "com.google.protobuf:protobuf-java" ], + "com.android.tools.build:builder-model": [ + "com.android.tools:annotations", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + ], + "com.android.tools.build:builder-test-api": [ + "com.android.tools.ddms:ddmlib", + "com.android.tools:annotations", + "com.android.tools:common", + "com.google.guava:guava" + ], + "com.android.tools.build:manifest-merger": [ + "com.android.tools:common", + "com.android.tools:sdk-common", + "com.android.tools:sdklib", + "com.google.code.gson:gson", + "net.sf.kxml:kxml2", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + ], + "com.android.tools.ddms:ddmlib": [ + "com.android.tools:common", + "com.google.protobuf:protobuf-java", + "net.sf.kxml:kxml2" + ], + "com.android.tools.external.com-intellij:intellij-core": [ + "org.jetbrains.intellij.deps:trove4j" + ], + "com.android.tools.layoutlib:layoutlib-api": [ + "com.android.tools:annotations", + "com.android.tools:common", + "net.sf.kxml:kxml2", + "org.jetbrains:annotations" + ], + "com.android.tools.lint:lint-api": [ + "com.android.tools.build:builder-model", + "com.android.tools.build:manifest-merger", + "com.android.tools.external.com-intellij:intellij-core", + "com.android.tools.external.com-intellij:kotlin-compiler", + "com.android.tools.external.org-jetbrains:uast", + "com.android.tools.layoutlib:layoutlib-api", + "com.android.tools.lint:lint-model", + "com.android.tools:common", + "com.android.tools:sdk-common", + "com.android.tools:sdklib", + "com.google.guava:guava", + "commons-io:commons-io", + "net.sf.kxml:kxml2", + "org.jetbrains.kotlin:kotlin-reflect", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8", + "org.ow2.asm:asm", + "org.ow2.asm:asm-tree" + ], + "com.android.tools.lint:lint-checks": [ + "com.android.tools.external.com-intellij:intellij-core", + "com.android.tools.external.com-intellij:kotlin-compiler", + "com.android.tools.external.org-jetbrains:uast", + "com.android.tools.layoutlib:layoutlib-api", + "com.android.tools.lint:lint-api", + "com.android.tools:common", + "com.android.tools:sdk-common", + "com.android.tools:sdklib", + "com.google.code.gson:gson", + "com.google.guava:guava", + "net.sf.kxml:kxml2", + "org.jetbrains.intellij.deps:trove4j", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8", + "org.ow2.asm:asm-analysis" + ], + "com.android.tools.lint:lint-model": [ + "com.android.tools.build:builder-model", + "com.android.tools:common", + "com.android.tools:sdk-common", + "net.sf.kxml:kxml2", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + ], + "com.android.tools:common": [ + "com.android.tools:annotations", + "com.google.guava:guava", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + ], + "com.android.tools:dvlib": [ + "com.android.tools:common" + ], + "com.android.tools:repository": [ + "com.android.tools.analytics-library:shared", + "com.android.tools:common", + "com.google.jimfs:jimfs", + "com.sun.activation:javax.activation", + "org.apache.commons:commons-compress", + "org.glassfish.jaxb:jaxb-runtime", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + ], + "com.android.tools:sdk-common": [ + "com.android.tools.analytics-library:shared", + "com.android.tools.build:aapt2-proto", + "com.android.tools.build:builder-model", + "com.android.tools.build:builder-test-api", + "com.android.tools.ddms:ddmlib", + "com.android.tools.layoutlib:layoutlib-api", + "com.android.tools:common", + "com.android.tools:sdklib", + "com.google.code.gson:gson", + "com.google.guava:guava", + "com.google.protobuf:protobuf-java", + "javax.inject:javax.inject", + "net.sf.kxml:kxml2", + "org.bouncycastle:bcpkix-jdk15on", + "org.bouncycastle:bcprov-jdk15on", + "org.glassfish.jaxb:jaxb-runtime", + "org.jetbrains.intellij.deps:trove4j", + "org.jetbrains.kotlin:kotlin-reflect", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8", + "xerces:xercesImpl" + ], + "com.android.tools:sdklib": [ + "com.android.tools.layoutlib:layoutlib-api", + "com.android.tools:common", + "com.android.tools:dvlib", + "com.android.tools:repository", + "com.google.code.gson:gson", + "org.apache.commons:commons-compress", + "org.apache.httpcomponents:httpcore", + "org.apache.httpcomponents:httpmime", + "org.glassfish.jaxb:jaxb-runtime" + ], "com.crashlytics.sdk.android:answers:aar": [ "io.fabric.sdk.android:fabric:aar" ], @@ -2237,6 +2647,9 @@ "com.google.j2objc:j2objc-annotations", "org.checkerframework:checker-qual" ], + "com.google.jimfs:jimfs": [ + "com.google.guava:guava" + ], "com.google.protobuf:protobuf-java-util": [ "com.google.code.gson:gson", "com.google.errorprone:error_prone_annotations", @@ -2305,6 +2718,9 @@ "org.jetbrains.kotlin:kotlin-reflect", "org.jetbrains.kotlin:kotlin-stdlib-jdk8" ], + "com.sun.istack:istack-commons-runtime": [ + "jakarta.activation:jakarta.activation-api" + ], "io.grpc:grpc-android:aar": [ "com.google.guava:guava", "io.grpc:grpc-core" @@ -2342,15 +2758,40 @@ "com.google.guava:guava", "io.grpc:grpc-api" ], + "jakarta.xml.bind:jakarta.xml.bind-api": [ + "jakarta.activation:jakarta.activation-api" + ], "junit:junit": [ "org.hamcrest:hamcrest-core" ], + "net.java.dev.jna:jna-platform": [ + "net.java.dev.jna:jna" + ], "nl.dionsegijn:konfetti:aar": [ "org.jetbrains.kotlin:kotlin-stdlib" ], + "org.apache.httpcomponents:httpclient": [ + "commons-codec:commons-codec", + "commons-logging:commons-logging", + "org.apache.httpcomponents:httpcore" + ], + "org.apache.httpcomponents:httpmime": [ + "org.apache.httpcomponents:httpclient" + ], + "org.bouncycastle:bcpkix-jdk15on": [ + "org.bouncycastle:bcprov-jdk15on" + ], "org.eclipse.parsson:parsson": [ "jakarta.json:jakarta.json-api" ], + "org.glassfish.jaxb:jaxb-runtime": [ + "com.sun.istack:istack-commons-runtime", + "com.sun.xml.fastinfoset:FastInfoset", + "jakarta.activation:jakarta.activation-api", + "jakarta.xml.bind:jakarta.xml.bind-api", + "org.glassfish.jaxb:txw2", + "org.jvnet.staxex:stax-ex" + ], "org.hamcrest:hamcrest-integration": [ "org.hamcrest:hamcrest-library" ], @@ -2420,6 +2861,10 @@ "org.jetbrains.kotlinx:kotlinx-metadata-jvm": [ "org.jetbrains.kotlin:kotlin-stdlib" ], + "org.jvnet.staxex:stax-ex": [ + "jakarta.activation:jakarta.activation-api", + "jakarta.xml.bind:jakarta.xml.bind-api" + ], "org.mockito.kotlin:mockito-kotlin": [ "org.mockito:mockito-core" ], @@ -2519,6 +2964,25 @@ "org.ow2.asm:asm-commons", "org.ow2.asm:asm-util", "org.robolectric:utils" + ], + "org.smali:baksmali": [ + "com.beust:jcommander", + "com.google.guava:guava", + "org.smali:dexlib2", + "org.smali:util" + ], + "org.smali:dexlib2": [ + "com.google.code.findbugs:jsr305", + "com.google.guava:guava", + "org.smali:util" + ], + "org.smali:util": [ + "com.beust:jcommander", + "com.google.code.findbugs:jsr305", + "com.google.guava:guava" + ], + "xerces:xercesImpl": [ + "xml-apis:xml-apis" ] }, "packages": { @@ -2577,6 +3041,24 @@ "com.android.databinding:baseLibrary": [ "android.databinding" ], + "com.android.tools.analytics-library:protos": [ + "com.android.tools.build.gradle.internal.profile", + "com.google.wireless.android.play.playlog.proto", + "com.google.wireless.android.sdk.stats" + ], + "com.android.tools.analytics-library:shared": [ + "com.android.tools.analytics" + ], + "com.android.tools.apkparser:apkanalyzer": [ + "com.android.tools.apk.analyzer", + "com.android.tools.apk.analyzer.dex", + "com.android.tools.apk.analyzer.dex.tree", + "com.android.tools.apk.analyzer.internal", + "com.android.tools.apk.analyzer.internal.rewriters" + ], + "com.android.tools.apkparser:binary-resources": [ + "com.google.devrel.gmscore.tools.apk.arsc" + ], "com.android.tools.build.jetifier:jetifier-core": [ "com.android.tools.build.jetifier.core", "com.android.tools.build.jetifier.core.config", @@ -2590,38 +3072,1165 @@ "android.aapt.pb.internal", "com.android.aapt" ], - "com.android.tools:annotations": [ - "com.android.annotations", - "com.android.annotations.concurrency" - ], - "com.github.bumptech.glide:annotations": [ - "com.bumptech.glide.annotation", - "com.bumptech.glide.annotation.compiler" - ], - "com.github.bumptech.glide:compiler": [ - "com.bumptech.glide.annotation.compiler", - "com.bumptech.glide.repackaged.com.google.common.base", - "com.bumptech.glide.repackaged.com.google.common.collect", - "com.bumptech.glide.repackaged.com.squareup.javapoet" - ], - "com.github.bumptech.glide:disklrucache": [ - "com.bumptech.glide.disklrucache" - ], - "com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework": [ - "com.google.android.apps.common.testing.accessibility.framework", - "com.google.android.apps.common.testing.accessibility.framework.integrations", - "com.google.android.apps.common.testing.accessibility.framework.integrations.espresso", - "com.googlecode.eyesfree.compat", - "com.googlecode.eyesfree.utils" - ], - "com.google.android.gms:strict-version-matcher-plugin": [ - "com.google.android.gms", - "com.google.android.gms.dependencies" - ], - "com.google.android:annotations": [ - "android.annotation" - ], - "com.google.auto.service:auto-service-annotations": [ + "com.android.tools.build:builder-model": [ + "com.android.build", + "com.android.builder.model", + "com.android.builder.model.level2", + "com.android.builder.model.v2", + "com.android.builder.model.v2.dsl", + "com.android.builder.model.v2.ide", + "com.android.builder.model.v2.models", + "com.android.builder.model.v2.models.ndk" + ], + "com.android.tools.build:builder-test-api": [ + "com.android.builder.testing.api" + ], + "com.android.tools.build:manifest-merger": [ + "com.android.manifmerger" + ], + "com.android.tools.ddms:ddmlib": [ + "com.android.commands.am", + "com.android.ddmlib", + "com.android.ddmlib.internal", + "com.android.ddmlib.internal.jdwp", + "com.android.ddmlib.internal.jdwp.chunkhandler", + "com.android.ddmlib.internal.jdwp.interceptor", + "com.android.ddmlib.jdwp", + "com.android.ddmlib.jdwp.packets", + "com.android.ddmlib.log", + "com.android.ddmlib.logcat", + "com.android.ddmlib.testrunner", + "com.android.ddmlib.utils", + "com.android.incfs.install", + "com.android.incfs.install.adb.ddmlib", + "com.android.server.adb.protos" + ], + "com.android.tools.external.com-intellij:intellij-core": [ + "com.android.tools.external.intellij.core", + "com.intellij", + "com.intellij.codeInsight", + "com.intellij.codeInsight.completion", + "com.intellij.codeInsight.completion.proc", + "com.intellij.codeInsight.completion.scope", + "com.intellij.codeInsight.controlflow", + "com.intellij.codeInsight.controlflow.impl", + "com.intellij.codeInsight.daemon.impl", + "com.intellij.codeInsight.daemon.impl.analysis", + "com.intellij.codeInsight.documentation", + "com.intellij.codeInsight.folding", + "com.intellij.codeInsight.folding.impl", + "com.intellij.codeInsight.generation", + "com.intellij.codeInsight.highlighting", + "com.intellij.codeInsight.javadoc", + "com.intellij.codeInsight.runner", + "com.intellij.codeInspection", + "com.intellij.codeWithMe", + "com.intellij.concurrency", + "com.intellij.core", + "com.intellij.diagnostic", + "com.intellij.diagnostic.tracing", + "com.intellij.execution", + "com.intellij.execution.configurations", + "com.intellij.execution.process", + "com.intellij.execution.rmi", + "com.intellij.execution.rmi.ssl", + "com.intellij.extapi.psi", + "com.intellij.formatting", + "com.intellij.icons", + "com.intellij.ide", + "com.intellij.ide.fileTemplates", + "com.intellij.ide.highlighter", + "com.intellij.ide.highlighter.custom", + "com.intellij.ide.highlighter.custom.tokens", + "com.intellij.ide.lightEdit", + "com.intellij.ide.plugins", + "com.intellij.ide.plugins.cl", + "com.intellij.ide.util", + "com.intellij.indentation", + "com.intellij.injected.editor", + "com.intellij.jna", + "com.intellij.lang", + "com.intellij.lang.folding", + "com.intellij.lang.impl", + "com.intellij.lang.injection", + "com.intellij.lang.java", + "com.intellij.lang.java.beans", + "com.intellij.lang.java.lexer", + "com.intellij.lang.java.parser", + "com.intellij.lang.java.source", + "com.intellij.lang.jvm", + "com.intellij.lang.jvm.annotation", + "com.intellij.lang.jvm.facade", + "com.intellij.lang.jvm.source", + "com.intellij.lang.jvm.types", + "com.intellij.lang.jvm.util", + "com.intellij.lang.spi", + "com.intellij.lexer", + "com.intellij.mock", + "com.intellij.model", + "com.intellij.model.presentation", + "com.intellij.model.psi", + "com.intellij.model.psi.impl", + "com.intellij.model.search", + "com.intellij.navigation", + "com.intellij.notebook.editor", + "com.intellij.openapi", + "com.intellij.openapi.application", + "com.intellij.openapi.application.ex", + "com.intellij.openapi.application.impl", + "com.intellij.openapi.command", + "com.intellij.openapi.command.impl", + "com.intellij.openapi.command.undo", + "com.intellij.openapi.components", + "com.intellij.openapi.diagnostic", + "com.intellij.openapi.diff", + "com.intellij.openapi.diff.ex", + "com.intellij.openapi.diff.impl", + "com.intellij.openapi.diff.impl.fragments", + "com.intellij.openapi.diff.impl.highlighting", + "com.intellij.openapi.diff.impl.patch", + "com.intellij.openapi.diff.impl.processing", + "com.intellij.openapi.diff.impl.string", + "com.intellij.openapi.diff.impl.util", + "com.intellij.openapi.editor", + "com.intellij.openapi.editor.actionSystem", + "com.intellij.openapi.editor.colors", + "com.intellij.openapi.editor.event", + "com.intellij.openapi.editor.ex", + "com.intellij.openapi.editor.ex.util", + "com.intellij.openapi.editor.impl", + "com.intellij.openapi.editor.impl.event", + "com.intellij.openapi.editor.markup", + "com.intellij.openapi.extensions", + "com.intellij.openapi.extensions.impl", + "com.intellij.openapi.fileEditor", + "com.intellij.openapi.fileEditor.impl", + "com.intellij.openapi.fileTypes", + "com.intellij.openapi.fileTypes.ex", + "com.intellij.openapi.fileTypes.impl", + "com.intellij.openapi.module", + "com.intellij.openapi.module.impl", + "com.intellij.openapi.options", + "com.intellij.openapi.progress", + "com.intellij.openapi.progress.impl", + "com.intellij.openapi.progress.util", + "com.intellij.openapi.project", + "com.intellij.openapi.projectRoots", + "com.intellij.openapi.roots", + "com.intellij.openapi.roots.impl", + "com.intellij.openapi.startup", + "com.intellij.openapi.ui", + "com.intellij.openapi.util", + "com.intellij.openapi.util.io", + "com.intellij.openapi.util.io.win32", + "com.intellij.openapi.util.objectTree", + "com.intellij.openapi.util.registry", + "com.intellij.openapi.util.text", + "com.intellij.openapi.vfs", + "com.intellij.openapi.vfs.encoding", + "com.intellij.openapi.vfs.ex", + "com.intellij.openapi.vfs.ex.http", + "com.intellij.openapi.vfs.impl", + "com.intellij.openapi.vfs.impl.http", + "com.intellij.openapi.vfs.impl.jar", + "com.intellij.openapi.vfs.local", + "com.intellij.openapi.vfs.newvfs", + "com.intellij.openapi.vfs.newvfs.events", + "com.intellij.openapi.vfs.pointers", + "com.intellij.openapi.wm.ex", + "com.intellij.patterns", + "com.intellij.patterns.compiler", + "com.intellij.pom", + "com.intellij.pom.core.impl", + "com.intellij.pom.event", + "com.intellij.pom.impl", + "com.intellij.pom.java", + "com.intellij.pom.references", + "com.intellij.pom.tree", + "com.intellij.pom.tree.events", + "com.intellij.pom.tree.events.impl", + "com.intellij.pom.wrappers", + "com.intellij.psi", + "com.intellij.psi.augment", + "com.intellij.psi.codeStyle", + "com.intellij.psi.compiled", + "com.intellij.psi.controlFlow", + "com.intellij.psi.css", + "com.intellij.psi.filters", + "com.intellij.psi.filters.classes", + "com.intellij.psi.filters.element", + "com.intellij.psi.filters.position", + "com.intellij.psi.impl", + "com.intellij.psi.impl.cache", + "com.intellij.psi.impl.compiled", + "com.intellij.psi.impl.file", + "com.intellij.psi.impl.file.impl", + "com.intellij.psi.impl.java.stubs", + "com.intellij.psi.impl.java.stubs.impl", + "com.intellij.psi.impl.java.stubs.index", + "com.intellij.psi.impl.light", + "com.intellij.psi.impl.meta", + "com.intellij.psi.impl.search", + "com.intellij.psi.impl.smartPointers", + "com.intellij.psi.impl.source", + "com.intellij.psi.impl.source.codeStyle", + "com.intellij.psi.impl.source.javadoc", + "com.intellij.psi.impl.source.resolve", + "com.intellij.psi.impl.source.resolve.graphInference", + "com.intellij.psi.impl.source.resolve.graphInference.constraints", + "com.intellij.psi.impl.source.resolve.reference", + "com.intellij.psi.impl.source.resolve.reference.impl", + "com.intellij.psi.impl.source.resolve.reference.impl.manipulators", + "com.intellij.psi.impl.source.resolve.reference.impl.providers", + "com.intellij.psi.impl.source.tree", + "com.intellij.psi.impl.source.tree.injected", + "com.intellij.psi.impl.source.tree.java", + "com.intellij.psi.infos", + "com.intellij.psi.javadoc", + "com.intellij.psi.meta", + "com.intellij.psi.presentation.java", + "com.intellij.psi.scope", + "com.intellij.psi.scope.conflictResolvers", + "com.intellij.psi.scope.processor", + "com.intellij.psi.scope.util", + "com.intellij.psi.search", + "com.intellij.psi.search.searches", + "com.intellij.psi.stub", + "com.intellij.psi.stubs", + "com.intellij.psi.targets", + "com.intellij.psi.templateLanguages", + "com.intellij.psi.text", + "com.intellij.psi.tree", + "com.intellij.psi.tree.java", + "com.intellij.psi.util", + "com.intellij.refactoring.rename", + "com.intellij.refactoring.util", + "com.intellij.reference", + "com.intellij.security", + "com.intellij.serialization", + "com.intellij.serviceContainer", + "com.intellij.testFramework", + "com.intellij.testIntegration", + "com.intellij.ui", + "com.intellij.ui.icons", + "com.intellij.usageView", + "com.intellij.util", + "com.intellij.util.cls", + "com.intellij.util.codeInsight", + "com.intellij.util.concurrency", + "com.intellij.util.concurrency.annotations", + "com.intellij.util.containers", + "com.intellij.util.containers.hash", + "com.intellij.util.diff", + "com.intellij.util.enumeration", + "com.intellij.util.exception", + "com.intellij.util.execution", + "com.intellij.util.graph", + "com.intellij.util.graph.impl", + "com.intellij.util.indexing", + "com.intellij.util.indexing.containers", + "com.intellij.util.indexing.flavor", + "com.intellij.util.indexing.impl", + "com.intellij.util.indexing.impl.forward", + "com.intellij.util.io", + "com.intellij.util.io.externalizer", + "com.intellij.util.io.keyStorage", + "com.intellij.util.io.storage", + "com.intellij.util.io.zip", + "com.intellij.util.keyFMap", + "com.intellij.util.lang", + "com.intellij.util.lang.fastutil", + "com.intellij.util.loader", + "com.intellij.util.messages", + "com.intellij.util.messages.impl", + "com.intellij.util.pico", + "com.intellij.util.profiling", + "com.intellij.util.properties", + "com.intellij.util.ref", + "com.intellij.util.system", + "com.intellij.util.text", + "com.intellij.util.ui", + "com.intellij.util.xmlb", + "com.intellij.util.xmlb.annotations", + "com.intellij.xml", + "com.intellij.xml.util", + "com.sun.jna", + "com.sun.jna.internal", + "com.sun.jna.platform", + "com.sun.jna.platform.dnd", + "com.sun.jna.platform.linux", + "com.sun.jna.platform.mac", + "com.sun.jna.platform.unix", + "com.sun.jna.platform.unix.aix", + "com.sun.jna.platform.unix.solaris", + "com.sun.jna.platform.win32", + "com.sun.jna.platform.win32.COM", + "com.sun.jna.platform.win32.COM.tlb", + "com.sun.jna.platform.win32.COM.tlb.imp", + "com.sun.jna.platform.win32.COM.util", + "com.sun.jna.platform.win32.COM.util.annotation", + "com.sun.jna.platform.wince", + "com.sun.jna.ptr", + "com.sun.jna.win32", + "it.unimi.dsi.fastutil", + "it.unimi.dsi.fastutil.booleans", + "it.unimi.dsi.fastutil.bytes", + "it.unimi.dsi.fastutil.chars", + "it.unimi.dsi.fastutil.doubles", + "it.unimi.dsi.fastutil.floats", + "it.unimi.dsi.fastutil.ints", + "it.unimi.dsi.fastutil.longs", + "it.unimi.dsi.fastutil.objects", + "it.unimi.dsi.fastutil.shorts", + "javaslang", + "javaslang.collection", + "javaslang.concurrent", + "javaslang.control", + "javaslang.match", + "javaslang.match.annotation", + "javaslang.match.generator", + "javaslang.match.model", + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta", + "javax.inject", + "net.jpountz.lz4", + "net.jpountz.util", + "net.jpountz.xxhash", + "one.util.streamex", + "org.apache.log4j", + "org.apache.log4j.chainsaw", + "org.apache.log4j.config", + "org.apache.log4j.helpers", + "org.apache.log4j.jdbc", + "org.apache.log4j.jmx", + "org.apache.log4j.lf5", + "org.apache.log4j.lf5.util", + "org.apache.log4j.lf5.viewer", + "org.apache.log4j.lf5.viewer.categoryexplorer", + "org.apache.log4j.lf5.viewer.configure", + "org.apache.log4j.net", + "org.apache.log4j.nt", + "org.apache.log4j.or", + "org.apache.log4j.or.jms", + "org.apache.log4j.or.sax", + "org.apache.log4j.pattern", + "org.apache.log4j.rewrite", + "org.apache.log4j.spi", + "org.apache.log4j.varia", + "org.apache.log4j.xml", + "org.apache.oro.io", + "org.apache.oro.text", + "org.apache.oro.text.awk", + "org.apache.oro.text.perl", + "org.apache.oro.text.regex", + "org.apache.oro.util", + "org.fusesource.hawtjni.runtime", + "org.fusesource.jansi", + "org.fusesource.jansi.internal", + "org.intellij.lang.annotations", + "org.jdom", + "org.jdom.adapters", + "org.jdom.filter", + "org.jdom.filter2", + "org.jdom.input", + "org.jdom.input.sax", + "org.jdom.input.stax", + "org.jdom.internal", + "org.jdom.located", + "org.jdom.output", + "org.jdom.output.support", + "org.jdom.transform", + "org.jdom.util", + "org.jdom.xpath", + "org.jdom.xpath.jaxen", + "org.jdom.xpath.util", + "org.jetbrains.annotations", + "org.jetbrains.concurrency", + "org.jetbrains.ide", + "org.jetbrains.jps", + "org.jetbrains.jps.plugin", + "org.jetbrains.jps.service", + "org.jetbrains.jps.service.impl", + "org.jetbrains.jps.util", + "org.jetbrains.org.objectweb.asm", + "org.jetbrains.org.objectweb.asm.commons", + "org.jetbrains.org.objectweb.asm.signature", + "org.jetbrains.org.objectweb.asm.tree", + "org.jetbrains.org.objectweb.asm.tree.analysis", + "org.jetbrains.org.objectweb.asm.util", + "org.jline.builtins", + "org.jline.builtins.ssh", + "org.jline.builtins.telnet", + "org.jline.keymap", + "org.jline.reader", + "org.jline.reader.impl", + "org.jline.reader.impl.completer", + "org.jline.reader.impl.history", + "org.jline.terminal", + "org.jline.terminal.impl", + "org.jline.terminal.impl.jansi", + "org.jline.terminal.impl.jansi.freebsd", + "org.jline.terminal.impl.jansi.linux", + "org.jline.terminal.impl.jansi.osx", + "org.jline.terminal.impl.jansi.solaris", + "org.jline.terminal.impl.jansi.win", + "org.jline.terminal.impl.jna", + "org.jline.terminal.impl.jna.freebsd", + "org.jline.terminal.impl.jna.linux", + "org.jline.terminal.impl.jna.osx", + "org.jline.terminal.impl.jna.solaris", + "org.jline.terminal.impl.jna.win", + "org.jline.terminal.spi", + "org.jline.utils", + "org.picocontainer", + "org.picocontainer.defaults" + ], + "com.android.tools.external.com-intellij:kotlin-compiler": [ + "com.android.tools.external.kotlin.compiler", + "kotlin.script.dependencies", + "kotlin.script.experimental.annotations", + "kotlin.script.experimental.api", + "kotlin.script.experimental.dependencies", + "kotlin.script.experimental.host", + "kotlin.script.experimental.jvm", + "kotlin.script.experimental.jvm.compat", + "kotlin.script.experimental.jvm.impl", + "kotlin.script.experimental.jvm.util", + "kotlin.script.experimental.location", + "kotlin.script.experimental.util", + "kotlin.script.extensions", + "kotlin.script.templates", + "kotlin.script.templates.standard", + "kotlinx.collections.immutable", + "kotlinx.collections.immutable.adapters", + "kotlinx.collections.immutable.implementations.immutableList", + "kotlinx.collections.immutable.implementations.immutableMap", + "kotlinx.collections.immutable.implementations.immutableSet", + "kotlinx.collections.immutable.implementations.persistentOrderedMap", + "kotlinx.collections.immutable.implementations.persistentOrderedSet", + "kotlinx.collections.immutable.internal", + "org.jetbrains.jps.model", + "org.jetbrains.jps.model.artifact", + "org.jetbrains.jps.model.artifact.elements", + "org.jetbrains.jps.model.artifact.elements.ex", + "org.jetbrains.jps.model.artifact.impl", + "org.jetbrains.jps.model.artifact.impl.elements", + "org.jetbrains.jps.model.ex", + "org.jetbrains.jps.model.fileTypes", + "org.jetbrains.jps.model.fileTypes.impl", + "org.jetbrains.jps.model.impl", + "org.jetbrains.jps.model.impl.runConfiguration", + "org.jetbrains.jps.model.jarRepository", + "org.jetbrains.jps.model.jarRepository.impl", + "org.jetbrains.jps.model.java", + "org.jetbrains.jps.model.java.compiler", + "org.jetbrains.jps.model.java.impl", + "org.jetbrains.jps.model.java.impl.compiler", + "org.jetbrains.jps.model.java.impl.runConfiguration", + "org.jetbrains.jps.model.java.runConfiguration", + "org.jetbrains.jps.model.library", + "org.jetbrains.jps.model.library.impl", + "org.jetbrains.jps.model.library.impl.sdk", + "org.jetbrains.jps.model.library.sdk", + "org.jetbrains.jps.model.module", + "org.jetbrains.jps.model.module.impl", + "org.jetbrains.jps.model.runConfiguration", + "org.jetbrains.jps.model.serialization", + "org.jetbrains.jps.model.serialization.artifact", + "org.jetbrains.jps.model.serialization.facet", + "org.jetbrains.jps.model.serialization.impl", + "org.jetbrains.jps.model.serialization.jarRepository", + "org.jetbrains.jps.model.serialization.java", + "org.jetbrains.jps.model.serialization.java.compiler", + "org.jetbrains.jps.model.serialization.library", + "org.jetbrains.jps.model.serialization.module", + "org.jetbrains.jps.model.serialization.runConfigurations", + "org.jetbrains.kotlin", + "org.jetbrains.kotlin.analyzer", + "org.jetbrains.kotlin.analyzer.common", + "org.jetbrains.kotlin.asJava", + "org.jetbrains.kotlin.asJava.builder", + "org.jetbrains.kotlin.asJava.classes", + "org.jetbrains.kotlin.asJava.elements", + "org.jetbrains.kotlin.asJava.finder", + "org.jetbrains.kotlin.backend.common", + "org.jetbrains.kotlin.backend.common.bridges", + "org.jetbrains.kotlin.backend.common.descriptors", + "org.jetbrains.kotlin.backend.common.extensions", + "org.jetbrains.kotlin.backend.common.ir", + "org.jetbrains.kotlin.backend.common.lower", + "org.jetbrains.kotlin.backend.common.lower.inline", + "org.jetbrains.kotlin.backend.common.lower.loops", + "org.jetbrains.kotlin.backend.common.lower.loops.handlers", + "org.jetbrains.kotlin.backend.common.lower.matchers", + "org.jetbrains.kotlin.backend.common.lower.optimizations", + "org.jetbrains.kotlin.backend.common.output", + "org.jetbrains.kotlin.backend.common.overrides", + "org.jetbrains.kotlin.backend.common.phaser", + "org.jetbrains.kotlin.backend.common.serialization", + "org.jetbrains.kotlin.backend.common.serialization.encodings", + "org.jetbrains.kotlin.backend.common.serialization.mangle", + "org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor", + "org.jetbrains.kotlin.backend.common.serialization.mangle.ir", + "org.jetbrains.kotlin.backend.common.serialization.metadata", + "org.jetbrains.kotlin.backend.common.serialization.metadata.impl", + "org.jetbrains.kotlin.backend.common.serialization.proto", + "org.jetbrains.kotlin.backend.common.serialization.signature", + "org.jetbrains.kotlin.backend.jvm", + "org.jetbrains.kotlin.backend.jvm.codegen", + "org.jetbrains.kotlin.backend.jvm.descriptors", + "org.jetbrains.kotlin.backend.jvm.intrinsics", + "org.jetbrains.kotlin.backend.jvm.ir", + "org.jetbrains.kotlin.backend.jvm.lower", + "org.jetbrains.kotlin.backend.jvm.lower.indy", + "org.jetbrains.kotlin.backend.jvm.lower.inlineclasses", + "org.jetbrains.kotlin.backend.jvm.serialization", + "org.jetbrains.kotlin.backend.wasm", + "org.jetbrains.kotlin.backend.wasm.ir2wasm", + "org.jetbrains.kotlin.backend.wasm.lower", + "org.jetbrains.kotlin.backend.wasm.utils", + "org.jetbrains.kotlin.build", + "org.jetbrains.kotlin.build.report", + "org.jetbrains.kotlin.build.report.metrics", + "org.jetbrains.kotlin.builtins", + "org.jetbrains.kotlin.builtins.functions", + "org.jetbrains.kotlin.builtins.jvm", + "org.jetbrains.kotlin.builtins.konan", + "org.jetbrains.kotlin.cfg", + "org.jetbrains.kotlin.cfg.pseudocode", + "org.jetbrains.kotlin.cfg.pseudocode.instructions", + "org.jetbrains.kotlin.cfg.pseudocode.instructions.eval", + "org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps", + "org.jetbrains.kotlin.cfg.pseudocode.instructions.special", + "org.jetbrains.kotlin.cfg.pseudocodeTraverser", + "org.jetbrains.kotlin.cfg.variable", + "org.jetbrains.kotlin.checkers", + "org.jetbrains.kotlin.checkers.diagnostics", + "org.jetbrains.kotlin.checkers.diagnostics.factories", + "org.jetbrains.kotlin.checkers.utils", + "org.jetbrains.kotlin.cli.common", + "org.jetbrains.kotlin.cli.common.arguments", + "org.jetbrains.kotlin.cli.common.config", + "org.jetbrains.kotlin.cli.common.environment", + "org.jetbrains.kotlin.cli.common.extensions", + "org.jetbrains.kotlin.cli.common.messages", + "org.jetbrains.kotlin.cli.common.modules", + "org.jetbrains.kotlin.cli.common.output", + "org.jetbrains.kotlin.cli.common.profiling", + "org.jetbrains.kotlin.cli.common.repl", + "org.jetbrains.kotlin.cli.js", + "org.jetbrains.kotlin.cli.js.dce", + "org.jetbrains.kotlin.cli.js.internal", + "org.jetbrains.kotlin.cli.jvm", + "org.jetbrains.kotlin.cli.jvm.compiler", + "org.jetbrains.kotlin.cli.jvm.config", + "org.jetbrains.kotlin.cli.jvm.index", + "org.jetbrains.kotlin.cli.jvm.javac", + "org.jetbrains.kotlin.cli.jvm.modules", + "org.jetbrains.kotlin.cli.jvm.plugins", + "org.jetbrains.kotlin.cli.metadata", + "org.jetbrains.kotlin.codegen", + "org.jetbrains.kotlin.codegen.binding", + "org.jetbrains.kotlin.codegen.context", + "org.jetbrains.kotlin.codegen.coroutines", + "org.jetbrains.kotlin.codegen.extensions", + "org.jetbrains.kotlin.codegen.inline", + "org.jetbrains.kotlin.codegen.inline.coroutines", + "org.jetbrains.kotlin.codegen.intrinsics", + "org.jetbrains.kotlin.codegen.optimization", + "org.jetbrains.kotlin.codegen.optimization.boxing", + "org.jetbrains.kotlin.codegen.optimization.common", + "org.jetbrains.kotlin.codegen.optimization.fixStack", + "org.jetbrains.kotlin.codegen.optimization.nullCheck", + "org.jetbrains.kotlin.codegen.optimization.transformer", + "org.jetbrains.kotlin.codegen.pseudoInsns", + "org.jetbrains.kotlin.codegen.range", + "org.jetbrains.kotlin.codegen.range.comparison", + "org.jetbrains.kotlin.codegen.range.forLoop", + "org.jetbrains.kotlin.codegen.range.inExpression", + "org.jetbrains.kotlin.codegen.serialization", + "org.jetbrains.kotlin.codegen.signature", + "org.jetbrains.kotlin.codegen.state", + "org.jetbrains.kotlin.codegen.when", + "org.jetbrains.kotlin.compiler.plugin", + "org.jetbrains.kotlin.compilerRunner", + "org.jetbrains.kotlin.config", + "org.jetbrains.kotlin.container", + "org.jetbrains.kotlin.context", + "org.jetbrains.kotlin.contracts", + "org.jetbrains.kotlin.contracts.description", + "org.jetbrains.kotlin.contracts.description.expressions", + "org.jetbrains.kotlin.contracts.interpretation", + "org.jetbrains.kotlin.contracts.model", + "org.jetbrains.kotlin.contracts.model.functors", + "org.jetbrains.kotlin.contracts.model.structure", + "org.jetbrains.kotlin.contracts.model.visitors", + "org.jetbrains.kotlin.contracts.parsing", + "org.jetbrains.kotlin.contracts.parsing.effects", + "org.jetbrains.kotlin.coroutines", + "org.jetbrains.kotlin.descriptors", + "org.jetbrains.kotlin.descriptors.annotations", + "org.jetbrains.kotlin.descriptors.deserialization", + "org.jetbrains.kotlin.descriptors.impl", + "org.jetbrains.kotlin.descriptors.java", + "org.jetbrains.kotlin.descriptors.konan", + "org.jetbrains.kotlin.descriptors.konan.impl", + "org.jetbrains.kotlin.descriptors.runtime.components", + "org.jetbrains.kotlin.descriptors.runtime.structure", + "org.jetbrains.kotlin.descriptors.synthetic", + "org.jetbrains.kotlin.diagnostics", + "org.jetbrains.kotlin.diagnostics.rendering", + "org.jetbrains.kotlin.extensions", + "org.jetbrains.kotlin.extensions.internal", + "org.jetbrains.kotlin.fileClasses", + "org.jetbrains.kotlin.fir", + "org.jetbrains.kotlin.fir.analysis", + "org.jetbrains.kotlin.fir.analysis.cfa", + "org.jetbrains.kotlin.fir.analysis.checkers", + "org.jetbrains.kotlin.fir.analysis.checkers.cfa", + "org.jetbrains.kotlin.fir.analysis.checkers.context", + "org.jetbrains.kotlin.fir.analysis.checkers.declaration", + "org.jetbrains.kotlin.fir.analysis.checkers.expression", + "org.jetbrains.kotlin.fir.analysis.checkers.extended", + "org.jetbrains.kotlin.fir.analysis.collectors", + "org.jetbrains.kotlin.fir.analysis.collectors.components", + "org.jetbrains.kotlin.fir.analysis.diagnostics", + "org.jetbrains.kotlin.fir.analysis.extensions", + "org.jetbrains.kotlin.fir.backend", + "org.jetbrains.kotlin.fir.backend.evaluate", + "org.jetbrains.kotlin.fir.backend.generators", + "org.jetbrains.kotlin.fir.backend.jvm", + "org.jetbrains.kotlin.fir.builder", + "org.jetbrains.kotlin.fir.caches", + "org.jetbrains.kotlin.fir.checkers", + "org.jetbrains.kotlin.fir.contracts", + "org.jetbrains.kotlin.fir.contracts.builder", + "org.jetbrains.kotlin.fir.contracts.description", + "org.jetbrains.kotlin.fir.contracts.impl", + "org.jetbrains.kotlin.fir.declarations", + "org.jetbrains.kotlin.fir.declarations.builder", + "org.jetbrains.kotlin.fir.declarations.impl", + "org.jetbrains.kotlin.fir.declarations.synthetic", + "org.jetbrains.kotlin.fir.descriptors", + "org.jetbrains.kotlin.fir.deserialization", + "org.jetbrains.kotlin.fir.diagnostics", + "org.jetbrains.kotlin.fir.expressions", + "org.jetbrains.kotlin.fir.expressions.builder", + "org.jetbrains.kotlin.fir.expressions.impl", + "org.jetbrains.kotlin.fir.extensions", + "org.jetbrains.kotlin.fir.extensions.predicate", + "org.jetbrains.kotlin.fir.impl", + "org.jetbrains.kotlin.fir.java", + "org.jetbrains.kotlin.fir.java.declarations", + "org.jetbrains.kotlin.fir.java.deserialization", + "org.jetbrains.kotlin.fir.java.enhancement", + "org.jetbrains.kotlin.fir.java.scopes", + "org.jetbrains.kotlin.fir.lazy", + "org.jetbrains.kotlin.fir.lightTree", + "org.jetbrains.kotlin.fir.lightTree.converter", + "org.jetbrains.kotlin.fir.lightTree.fir", + "org.jetbrains.kotlin.fir.lightTree.fir.modifier", + "org.jetbrains.kotlin.fir.references", + "org.jetbrains.kotlin.fir.references.builder", + "org.jetbrains.kotlin.fir.references.impl", + "org.jetbrains.kotlin.fir.resolve", + "org.jetbrains.kotlin.fir.resolve.calls", + "org.jetbrains.kotlin.fir.resolve.calls.jvm", + "org.jetbrains.kotlin.fir.resolve.calls.tower", + "org.jetbrains.kotlin.fir.resolve.dfa", + "org.jetbrains.kotlin.fir.resolve.dfa.cfg", + "org.jetbrains.kotlin.fir.resolve.dfa.contracts", + "org.jetbrains.kotlin.fir.resolve.diagnostics", + "org.jetbrains.kotlin.fir.resolve.inference", + "org.jetbrains.kotlin.fir.resolve.inference.model", + "org.jetbrains.kotlin.fir.resolve.providers", + "org.jetbrains.kotlin.fir.resolve.providers.impl", + "org.jetbrains.kotlin.fir.resolve.scopes", + "org.jetbrains.kotlin.fir.resolve.substitution", + "org.jetbrains.kotlin.fir.resolve.transformers", + "org.jetbrains.kotlin.fir.resolve.transformers.body.resolve", + "org.jetbrains.kotlin.fir.resolve.transformers.contracts", + "org.jetbrains.kotlin.fir.resolve.transformers.plugin", + "org.jetbrains.kotlin.fir.scopes", + "org.jetbrains.kotlin.fir.scopes.impl", + "org.jetbrains.kotlin.fir.scopes.jvm", + "org.jetbrains.kotlin.fir.serialization", + "org.jetbrains.kotlin.fir.serialization.constant", + "org.jetbrains.kotlin.fir.session", + "org.jetbrains.kotlin.fir.signaturer", + "org.jetbrains.kotlin.fir.symbols", + "org.jetbrains.kotlin.fir.symbols.impl", + "org.jetbrains.kotlin.fir.types", + "org.jetbrains.kotlin.fir.types.builder", + "org.jetbrains.kotlin.fir.types.impl", + "org.jetbrains.kotlin.fir.types.jvm", + "org.jetbrains.kotlin.fir.utils", + "org.jetbrains.kotlin.fir.visitors", + "org.jetbrains.kotlin.frontend.di", + "org.jetbrains.kotlin.frontend.java.di", + "org.jetbrains.kotlin.frontend.js.di", + "org.jetbrains.kotlin.idea", + "org.jetbrains.kotlin.incremental", + "org.jetbrains.kotlin.incremental.components", + "org.jetbrains.kotlin.incremental.js", + "org.jetbrains.kotlin.incremental.multiproject", + "org.jetbrains.kotlin.incremental.parsing", + "org.jetbrains.kotlin.incremental.snapshots", + "org.jetbrains.kotlin.incremental.storage", + "org.jetbrains.kotlin.incremental.util", + "org.jetbrains.kotlin.inline", + "org.jetbrains.kotlin.ir", + "org.jetbrains.kotlin.ir.backend.js", + "org.jetbrains.kotlin.ir.backend.js.export", + "org.jetbrains.kotlin.ir.backend.js.ir", + "org.jetbrains.kotlin.ir.backend.js.lower", + "org.jetbrains.kotlin.ir.backend.js.lower.calls", + "org.jetbrains.kotlin.ir.backend.js.lower.cleanup", + "org.jetbrains.kotlin.ir.backend.js.lower.coroutines", + "org.jetbrains.kotlin.ir.backend.js.lower.inline", + "org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir", + "org.jetbrains.kotlin.ir.backend.js.transformers.irToJs", + "org.jetbrains.kotlin.ir.backend.js.utils", + "org.jetbrains.kotlin.ir.backend.jvm", + "org.jetbrains.kotlin.ir.backend.jvm.serialization", + "org.jetbrains.kotlin.ir.builders", + "org.jetbrains.kotlin.ir.builders.declarations", + "org.jetbrains.kotlin.ir.declarations", + "org.jetbrains.kotlin.ir.declarations.impl", + "org.jetbrains.kotlin.ir.declarations.lazy", + "org.jetbrains.kotlin.ir.declarations.persistent", + "org.jetbrains.kotlin.ir.declarations.persistent.carriers", + "org.jetbrains.kotlin.ir.descriptors", + "org.jetbrains.kotlin.ir.expressions", + "org.jetbrains.kotlin.ir.expressions.impl", + "org.jetbrains.kotlin.ir.expressions.persistent", + "org.jetbrains.kotlin.ir.interpreter", + "org.jetbrains.kotlin.ir.interpreter.builtins", + "org.jetbrains.kotlin.ir.interpreter.exceptions", + "org.jetbrains.kotlin.ir.interpreter.intrinsics", + "org.jetbrains.kotlin.ir.interpreter.stack", + "org.jetbrains.kotlin.ir.interpreter.state", + "org.jetbrains.kotlin.ir.linkage", + "org.jetbrains.kotlin.ir.overrides", + "org.jetbrains.kotlin.ir.symbols", + "org.jetbrains.kotlin.ir.symbols.impl", + "org.jetbrains.kotlin.ir.types", + "org.jetbrains.kotlin.ir.types.impl", + "org.jetbrains.kotlin.ir.util", + "org.jetbrains.kotlin.ir.visitors", + "org.jetbrains.kotlin.javac", + "org.jetbrains.kotlin.javac.components", + "org.jetbrains.kotlin.javac.resolve", + "org.jetbrains.kotlin.javac.wrappers.symbols", + "org.jetbrains.kotlin.javac.wrappers.trees", + "org.jetbrains.kotlin.js", + "org.jetbrains.kotlin.js.analyze", + "org.jetbrains.kotlin.js.analyzer", + "org.jetbrains.kotlin.js.backend", + "org.jetbrains.kotlin.js.backend.ast", + "org.jetbrains.kotlin.js.backend.ast.metadata", + "org.jetbrains.kotlin.js.common", + "org.jetbrains.kotlin.js.config", + "org.jetbrains.kotlin.js.coroutine", + "org.jetbrains.kotlin.js.dce", + "org.jetbrains.kotlin.js.descriptorUtils", + "org.jetbrains.kotlin.js.facade", + "org.jetbrains.kotlin.js.facade.exceptions", + "org.jetbrains.kotlin.js.inline", + "org.jetbrains.kotlin.js.inline.clean", + "org.jetbrains.kotlin.js.inline.context", + "org.jetbrains.kotlin.js.inline.util", + "org.jetbrains.kotlin.js.inline.util.collectors", + "org.jetbrains.kotlin.js.inline.util.rewriters", + "org.jetbrains.kotlin.js.naming", + "org.jetbrains.kotlin.js.parser", + "org.jetbrains.kotlin.js.parser.sourcemaps", + "org.jetbrains.kotlin.js.patterns", + "org.jetbrains.kotlin.js.patterns.typePredicates", + "org.jetbrains.kotlin.js.resolve", + "org.jetbrains.kotlin.js.resolve.diagnostics", + "org.jetbrains.kotlin.js.sourceMap", + "org.jetbrains.kotlin.js.translate.callTranslator", + "org.jetbrains.kotlin.js.translate.context", + "org.jetbrains.kotlin.js.translate.context.generator", + "org.jetbrains.kotlin.js.translate.declaration", + "org.jetbrains.kotlin.js.translate.expression", + "org.jetbrains.kotlin.js.translate.extensions", + "org.jetbrains.kotlin.js.translate.general", + "org.jetbrains.kotlin.js.translate.initializer", + "org.jetbrains.kotlin.js.translate.intrinsic", + "org.jetbrains.kotlin.js.translate.intrinsic.functions", + "org.jetbrains.kotlin.js.translate.intrinsic.functions.basic", + "org.jetbrains.kotlin.js.translate.intrinsic.functions.factories", + "org.jetbrains.kotlin.js.translate.intrinsic.objects", + "org.jetbrains.kotlin.js.translate.intrinsic.operation", + "org.jetbrains.kotlin.js.translate.operation", + "org.jetbrains.kotlin.js.translate.reference", + "org.jetbrains.kotlin.js.translate.test", + "org.jetbrains.kotlin.js.translate.utils", + "org.jetbrains.kotlin.js.translate.utils.jsAstUtils", + "org.jetbrains.kotlin.js.translate.utils.mutator", + "org.jetbrains.kotlin.js.util", + "org.jetbrains.kotlin.kapt3.diagnostic", + "org.jetbrains.kotlin.kdoc.lexer", + "org.jetbrains.kotlin.kdoc.parser", + "org.jetbrains.kotlin.kdoc.psi.api", + "org.jetbrains.kotlin.kdoc.psi.impl", + "org.jetbrains.kotlin.konan", + "org.jetbrains.kotlin.konan.file", + "org.jetbrains.kotlin.konan.library", + "org.jetbrains.kotlin.konan.properties", + "org.jetbrains.kotlin.konan.target", + "org.jetbrains.kotlin.konan.util", + "org.jetbrains.kotlin.lexer", + "org.jetbrains.kotlin.library", + "org.jetbrains.kotlin.library.impl", + "org.jetbrains.kotlin.library.metadata", + "org.jetbrains.kotlin.library.resolver", + "org.jetbrains.kotlin.library.resolver.impl", + "org.jetbrains.kotlin.load.java", + "org.jetbrains.kotlin.load.java.components", + "org.jetbrains.kotlin.load.java.descriptors", + "org.jetbrains.kotlin.load.java.lazy", + "org.jetbrains.kotlin.load.java.lazy.descriptors", + "org.jetbrains.kotlin.load.java.lazy.types", + "org.jetbrains.kotlin.load.java.sam", + "org.jetbrains.kotlin.load.java.sources", + "org.jetbrains.kotlin.load.java.structure", + "org.jetbrains.kotlin.load.java.structure.impl", + "org.jetbrains.kotlin.load.java.structure.impl.classFiles", + "org.jetbrains.kotlin.load.java.typeEnhancement", + "org.jetbrains.kotlin.load.kotlin", + "org.jetbrains.kotlin.load.kotlin.header", + "org.jetbrains.kotlin.load.kotlin.incremental", + "org.jetbrains.kotlin.load.kotlin.incremental.components", + "org.jetbrains.kotlin.metadata", + "org.jetbrains.kotlin.metadata.builtins", + "org.jetbrains.kotlin.metadata.deserialization", + "org.jetbrains.kotlin.metadata.java", + "org.jetbrains.kotlin.metadata.js", + "org.jetbrains.kotlin.metadata.jvm", + "org.jetbrains.kotlin.metadata.jvm.deserialization", + "org.jetbrains.kotlin.metadata.jvm.serialization", + "org.jetbrains.kotlin.metadata.serialization", + "org.jetbrains.kotlin.modules", + "org.jetbrains.kotlin.name", + "org.jetbrains.kotlin.parsing", + "org.jetbrains.kotlin.platform", + "org.jetbrains.kotlin.platform.js", + "org.jetbrains.kotlin.platform.jvm", + "org.jetbrains.kotlin.platform.konan", + "org.jetbrains.kotlin.progress", + "org.jetbrains.kotlin.progress.experimental", + "org.jetbrains.kotlin.protobuf", + "org.jetbrains.kotlin.psi", + "org.jetbrains.kotlin.psi.addRemoveModifier", + "org.jetbrains.kotlin.psi.codeFragmentUtil", + "org.jetbrains.kotlin.psi.debugText", + "org.jetbrains.kotlin.psi.findDocComment", + "org.jetbrains.kotlin.psi.psiUtil", + "org.jetbrains.kotlin.psi.stubs", + "org.jetbrains.kotlin.psi.stubs.elements", + "org.jetbrains.kotlin.psi.stubs.impl", + "org.jetbrains.kotlin.psi.synthetics", + "org.jetbrains.kotlin.psi.typeRefHelpers", + "org.jetbrains.kotlin.psi2ir", + "org.jetbrains.kotlin.psi2ir.generators", + "org.jetbrains.kotlin.psi2ir.intermediate", + "org.jetbrains.kotlin.psi2ir.transformations", + "org.jetbrains.kotlin.renderer", + "org.jetbrains.kotlin.resolve", + "org.jetbrains.kotlin.resolve.annotations", + "org.jetbrains.kotlin.resolve.bindingContextUtil", + "org.jetbrains.kotlin.resolve.calls", + "org.jetbrains.kotlin.resolve.calls.callResolverUtil", + "org.jetbrains.kotlin.resolve.calls.callUtil", + "org.jetbrains.kotlin.resolve.calls.checkers", + "org.jetbrains.kotlin.resolve.calls.components", + "org.jetbrains.kotlin.resolve.calls.context", + "org.jetbrains.kotlin.resolve.calls.inference", + "org.jetbrains.kotlin.resolve.calls.inference.components", + "org.jetbrains.kotlin.resolve.calls.inference.constraintPosition", + "org.jetbrains.kotlin.resolve.calls.inference.model", + "org.jetbrains.kotlin.resolve.calls.model", + "org.jetbrains.kotlin.resolve.calls.resolvedCallUtil", + "org.jetbrains.kotlin.resolve.calls.results", + "org.jetbrains.kotlin.resolve.calls.smartcasts", + "org.jetbrains.kotlin.resolve.calls.tasks", + "org.jetbrains.kotlin.resolve.calls.tower", + "org.jetbrains.kotlin.resolve.calls.util", + "org.jetbrains.kotlin.resolve.checkers", + "org.jetbrains.kotlin.resolve.constants", + "org.jetbrains.kotlin.resolve.constants.evaluate", + "org.jetbrains.kotlin.resolve.deprecation", + "org.jetbrains.kotlin.resolve.descriptorUtil", + "org.jetbrains.kotlin.resolve.diagnostics", + "org.jetbrains.kotlin.resolve.extensions", + "org.jetbrains.kotlin.resolve.inline", + "org.jetbrains.kotlin.resolve.jvm", + "org.jetbrains.kotlin.resolve.jvm.annotations", + "org.jetbrains.kotlin.resolve.jvm.checkers", + "org.jetbrains.kotlin.resolve.jvm.diagnostics", + "org.jetbrains.kotlin.resolve.jvm.extensions", + "org.jetbrains.kotlin.resolve.jvm.jvmSignature", + "org.jetbrains.kotlin.resolve.jvm.kotlinSignature", + "org.jetbrains.kotlin.resolve.jvm.modules", + "org.jetbrains.kotlin.resolve.jvm.multiplatform", + "org.jetbrains.kotlin.resolve.jvm.platform", + "org.jetbrains.kotlin.resolve.konan.diagnostics", + "org.jetbrains.kotlin.resolve.konan.platform", + "org.jetbrains.kotlin.resolve.lazy", + "org.jetbrains.kotlin.resolve.lazy.data", + "org.jetbrains.kotlin.resolve.lazy.declarations", + "org.jetbrains.kotlin.resolve.lazy.descriptors", + "org.jetbrains.kotlin.resolve.multiplatform", + "org.jetbrains.kotlin.resolve.repl", + "org.jetbrains.kotlin.resolve.sam", + "org.jetbrains.kotlin.resolve.scopes", + "org.jetbrains.kotlin.resolve.scopes.receivers", + "org.jetbrains.kotlin.resolve.scopes.synthetic", + "org.jetbrains.kotlin.resolve.scopes.utils", + "org.jetbrains.kotlin.resolve.source", + "org.jetbrains.kotlin.resolve.typeBinding", + "org.jetbrains.kotlin.scripting", + "org.jetbrains.kotlin.scripting.compiler.plugin", + "org.jetbrains.kotlin.scripting.compiler.plugin.definitions", + "org.jetbrains.kotlin.scripting.compiler.plugin.dependencies", + "org.jetbrains.kotlin.scripting.compiler.plugin.extensions", + "org.jetbrains.kotlin.scripting.compiler.plugin.impl", + "org.jetbrains.kotlin.scripting.compiler.plugin.repl", + "org.jetbrains.kotlin.scripting.compiler.plugin.repl.configuration", + "org.jetbrains.kotlin.scripting.compiler.plugin.repl.messages", + "org.jetbrains.kotlin.scripting.compiler.plugin.repl.reader", + "org.jetbrains.kotlin.scripting.compiler.plugin.repl.writer", + "org.jetbrains.kotlin.scripting.configuration", + "org.jetbrains.kotlin.scripting.definitions", + "org.jetbrains.kotlin.scripting.extensions", + "org.jetbrains.kotlin.scripting.repl.js", + "org.jetbrains.kotlin.scripting.resolve", + "org.jetbrains.kotlin.serialization", + "org.jetbrains.kotlin.serialization.builtins", + "org.jetbrains.kotlin.serialization.deserialization", + "org.jetbrains.kotlin.serialization.deserialization.builtins", + "org.jetbrains.kotlin.serialization.deserialization.descriptors", + "org.jetbrains.kotlin.serialization.js", + "org.jetbrains.kotlin.serialization.js.ast", + "org.jetbrains.kotlin.serialization.konan", + "org.jetbrains.kotlin.serialization.konan.impl", + "org.jetbrains.kotlin.storage", + "org.jetbrains.kotlin.synthetic", + "org.jetbrains.kotlin.type", + "org.jetbrains.kotlin.types", + "org.jetbrains.kotlin.types.checker", + "org.jetbrains.kotlin.types.error", + "org.jetbrains.kotlin.types.expressions", + "org.jetbrains.kotlin.types.expressions.typeInfoFactory", + "org.jetbrains.kotlin.types.expressions.unqualifiedSuper", + "org.jetbrains.kotlin.types.model", + "org.jetbrains.kotlin.types.refinement", + "org.jetbrains.kotlin.types.typeUtil", + "org.jetbrains.kotlin.types.typesApproximation", + "org.jetbrains.kotlin.util", + "org.jetbrains.kotlin.util.capitalizeDecapitalize", + "org.jetbrains.kotlin.util.collectionUtils", + "org.jetbrains.kotlin.util.javaslang", + "org.jetbrains.kotlin.util.slicedMap", + "org.jetbrains.kotlin.utils", + "org.jetbrains.kotlin.utils.addToStdlib", + "org.jetbrains.kotlin.utils.concurrent.block", + "org.jetbrains.kotlin.utils.fileUtils", + "org.jetbrains.kotlin.utils.intellij", + "org.jetbrains.kotlin.utils.kapt", + "org.jetbrains.kotlin.utils.repl", + "org.jetbrains.kotlin.utils.strings", + "org.jetbrains.kotlin.wasm.ir", + "org.jetbrains.kotlin.wasm.ir.convertors" + ], + "com.android.tools.external.org-jetbrains:uast": [ + "com.android.tools.external.org.jetbrains.uast", + "org.jetbrains.uast", + "org.jetbrains.uast.analysis", + "org.jetbrains.uast.evaluation", + "org.jetbrains.uast.expressions", + "org.jetbrains.uast.generate", + "org.jetbrains.uast.internal", + "org.jetbrains.uast.java", + "org.jetbrains.uast.java.analysis", + "org.jetbrains.uast.java.declarations", + "org.jetbrains.uast.java.expressions", + "org.jetbrains.uast.java.generate", + "org.jetbrains.uast.java.internal", + "org.jetbrains.uast.java.kinds", + "org.jetbrains.uast.kotlin", + "org.jetbrains.uast.kotlin.declarations", + "org.jetbrains.uast.kotlin.evaluation", + "org.jetbrains.uast.kotlin.expressions", + "org.jetbrains.uast.kotlin.internal", + "org.jetbrains.uast.kotlin.kinds", + "org.jetbrains.uast.kotlin.psi", + "org.jetbrains.uast.psi", + "org.jetbrains.uast.util", + "org.jetbrains.uast.values", + "org.jetbrains.uast.visitor", + "test.pkg" + ], + "com.android.tools.layoutlib:layoutlib-api": [ + "com.android.ide.common.rendering.api", + "com.android.resources" + ], + "com.android.tools.lint:lint-api": [ + "com.android.tools.lint.client.api", + "com.android.tools.lint.detector.api", + "com.android.tools.lint.detector.api.interprocedural", + "com.android.tools.lint.helpers" + ], + "com.android.tools.lint:lint-checks": [ + "com.android.tools.lint.checks" + ], + "com.android.tools.lint:lint-model": [ + "com.android.tools.lint.model" + ], + "com.android.tools:annotations": [ + "com.android.annotations", + "com.android.annotations.concurrency" + ], + "com.android.tools:common": [ + "com.android", + "com.android.ide.common.blame", + "com.android.io", + "com.android.prefs", + "com.android.resources", + "com.android.sdklib", + "com.android.support", + "com.android.tools.proguard", + "com.android.utils", + "com.android.utils.concurrency", + "com.android.utils.cxx", + "com.android.utils.reflection", + "com.android.xml" + ], + "com.android.tools:dvlib": [ + "com.android.dvlib" + ], + "com.android.tools:repository": [ + "com.android.repository", + "com.android.repository.api", + "com.android.repository.impl.downloader", + "com.android.repository.impl.generated.generic.v1", + "com.android.repository.impl.generated.generic.v2", + "com.android.repository.impl.generated.v1", + "com.android.repository.impl.generated.v2", + "com.android.repository.impl.installer", + "com.android.repository.impl.manager", + "com.android.repository.impl.meta", + "com.android.repository.impl.sources", + "com.android.repository.impl.sources.generated.v1", + "com.android.repository.io", + "com.android.repository.io.impl", + "com.android.repository.util" + ], + "com.android.tools:sdk-common": [ + "com.android.ide.common.attribution", + "com.android.ide.common.blame", + "com.android.ide.common.blame.parser", + "com.android.ide.common.blame.parser.aapt", + "com.android.ide.common.blame.parser.util", + "com.android.ide.common.build", + "com.android.ide.common.caching", + "com.android.ide.common.fonts", + "com.android.ide.common.internal", + "com.android.ide.common.process", + "com.android.ide.common.rendering", + "com.android.ide.common.repository", + "com.android.ide.common.resources", + "com.android.ide.common.resources.configuration", + "com.android.ide.common.resources.sampledata", + "com.android.ide.common.resources.usage", + "com.android.ide.common.sdk", + "com.android.ide.common.signing", + "com.android.ide.common.symbols", + "com.android.ide.common.util", + "com.android.ide.common.vectordrawable", + "com.android.ide.common.workers", + "com.android.ide.common.xml", + "com.android.instantapp.provision", + "com.android.instantapp.run", + "com.android.instantapp.sdk", + "com.android.instantapp.utils", + "com.android.projectmodel", + "wireless.android.instantapps.sdk" + ], + "com.android.tools:sdklib": [ + "com.android.sdklib", + "com.android.sdklib.devices", + "com.android.sdklib.internal.avd", + "com.android.sdklib.internal.build", + "com.android.sdklib.internal.project", + "com.android.sdklib.repository", + "com.android.sdklib.repository.generated.addon.v1", + "com.android.sdklib.repository.generated.addon.v2", + "com.android.sdklib.repository.generated.common.v1", + "com.android.sdklib.repository.generated.common.v2", + "com.android.sdklib.repository.generated.repository.v1", + "com.android.sdklib.repository.generated.repository.v2", + "com.android.sdklib.repository.generated.sysimg.v1", + "com.android.sdklib.repository.generated.sysimg.v2", + "com.android.sdklib.repository.installer", + "com.android.sdklib.repository.legacy", + "com.android.sdklib.repository.legacy.descriptors", + "com.android.sdklib.repository.legacy.local", + "com.android.sdklib.repository.legacy.remote", + "com.android.sdklib.repository.legacy.remote.internal", + "com.android.sdklib.repository.legacy.remote.internal.archives", + "com.android.sdklib.repository.legacy.remote.internal.packages", + "com.android.sdklib.repository.legacy.remote.internal.sources", + "com.android.sdklib.repository.meta", + "com.android.sdklib.repository.sources", + "com.android.sdklib.repository.sources.generated.v1", + "com.android.sdklib.repository.sources.generated.v2", + "com.android.sdklib.repository.sources.generated.v3", + "com.android.sdklib.repository.sources.generated.v4", + "com.android.sdklib.repository.targets", + "com.android.sdklib.tool", + "com.android.sdklib.tool.sdkmanager", + "com.android.sdklib.util" + ], + "com.beust:jcommander": [ + "com.beust.jcommander", + "com.beust.jcommander.converters", + "com.beust.jcommander.defaultprovider", + "com.beust.jcommander.internal", + "com.beust.jcommander.validators" + ], + "com.github.bumptech.glide:annotations": [ + "com.bumptech.glide.annotation", + "com.bumptech.glide.annotation.compiler" + ], + "com.github.bumptech.glide:compiler": [ + "com.bumptech.glide.annotation.compiler", + "com.bumptech.glide.repackaged.com.google.common.base", + "com.bumptech.glide.repackaged.com.google.common.collect", + "com.bumptech.glide.repackaged.com.squareup.javapoet" + ], + "com.github.bumptech.glide:disklrucache": [ + "com.bumptech.glide.disklrucache" + ], + "com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework": [ + "com.google.android.apps.common.testing.accessibility.framework", + "com.google.android.apps.common.testing.accessibility.framework.integrations", + "com.google.android.apps.common.testing.accessibility.framework.integrations.espresso", + "com.googlecode.eyesfree.compat", + "com.googlecode.eyesfree.utils" + ], + "com.google.android.gms:strict-version-matcher-plugin": [ + "com.google.android.gms", + "com.google.android.gms.dependencies" + ], + "com.google.android:annotations": [ + "android.annotation" + ], + "com.google.auto.service:auto-service-annotations": [ "com.google.auto.service" ], "com.google.auto.value:auto-value-annotations": [ @@ -2771,6 +4380,9 @@ "com.google.j2objc:j2objc-annotations": [ "com.google.j2objc.annotations" ], + "com.google.jimfs:jimfs": [ + "com.google.common.jimfs" + ], "com.google.protobuf:protobuf-java": [ "com.google.protobuf", "com.google.protobuf.compiler" @@ -2896,6 +4508,35 @@ "com.squareup.kotlinpoet.jvm", "com.squareup.kotlinpoet.tags" ], + "com.sun.activation:javax.activation": [ + "com.sun.activation.registries", + "com.sun.activation.viewers", + "javax.activation" + ], + "com.sun.istack:istack-commons-runtime": [ + "com.sun.istack", + "com.sun.istack.localization", + "com.sun.istack.logging" + ], + "com.sun.xml.fastinfoset:FastInfoset": [ + "com.sun.xml.fastinfoset", + "com.sun.xml.fastinfoset.algorithm", + "com.sun.xml.fastinfoset.alphabet", + "com.sun.xml.fastinfoset.dom", + "com.sun.xml.fastinfoset.org.apache.xerces.util", + "com.sun.xml.fastinfoset.sax", + "com.sun.xml.fastinfoset.stax", + "com.sun.xml.fastinfoset.stax.events", + "com.sun.xml.fastinfoset.stax.factory", + "com.sun.xml.fastinfoset.stax.util", + "com.sun.xml.fastinfoset.tools", + "com.sun.xml.fastinfoset.util", + "com.sun.xml.fastinfoset.vocab", + "org.jvnet.fastinfoset", + "org.jvnet.fastinfoset.sax", + "org.jvnet.fastinfoset.sax.helpers", + "org.jvnet.fastinfoset.stax" + ], "commons-codec:commons-codec": [ "org.apache.commons.codec", "org.apache.commons.codec.binary", @@ -2912,6 +4553,10 @@ "org.apache.commons.io.monitor", "org.apache.commons.io.output" ], + "commons-logging:commons-logging": [ + "org.apache.commons.logging", + "org.apache.commons.logging.impl" + ], "io.grpc:grpc-api": [ "io.grpc" ], @@ -2941,11 +4586,22 @@ "io.xlate:yaml-json": [ "io.xlate.yamljson" ], + "jakarta.activation:jakarta.activation-api": [ + "javax.activation" + ], "jakarta.json:jakarta.json-api": [ "jakarta.json", "jakarta.json.spi", "jakarta.json.stream" ], + "jakarta.xml.bind:jakarta.xml.bind-api": [ + "javax.xml.bind", + "javax.xml.bind.annotation", + "javax.xml.bind.annotation.adapters", + "javax.xml.bind.attachment", + "javax.xml.bind.helpers", + "javax.xml.bind.util" + ], "javax.annotation:javax.annotation-api": [ "javax.annotation", "javax.annotation.security", @@ -3029,6 +4685,27 @@ "net.bytebuddy:byte-buddy-agent": [ "net.bytebuddy.agent" ], + "net.java.dev.jna:jna": [ + "com.sun.jna", + "com.sun.jna.internal", + "com.sun.jna.ptr", + "com.sun.jna.win32" + ], + "net.java.dev.jna:jna-platform": [ + "com.sun.jna.platform", + "com.sun.jna.platform.dnd", + "com.sun.jna.platform.linux", + "com.sun.jna.platform.mac", + "com.sun.jna.platform.unix", + "com.sun.jna.platform.unix.solaris", + "com.sun.jna.platform.win32", + "com.sun.jna.platform.win32.COM", + "com.sun.jna.platform.win32.COM.tlb", + "com.sun.jna.platform.win32.COM.tlb.imp", + "com.sun.jna.platform.win32.COM.util", + "com.sun.jna.platform.win32.COM.util.annotation", + "com.sun.jna.platform.wince" + ], "net.ltgt.gradle.incap:incap": [ "net.ltgt.gradle.incap" ], @@ -3079,6 +4756,127 @@ "org.stringtemplate.v4.gui", "org.stringtemplate.v4.misc" ], + "org.apache.commons:commons-compress": [ + "org.apache.commons.compress", + "org.apache.commons.compress.archivers", + "org.apache.commons.compress.archivers.ar", + "org.apache.commons.compress.archivers.arj", + "org.apache.commons.compress.archivers.cpio", + "org.apache.commons.compress.archivers.dump", + "org.apache.commons.compress.archivers.examples", + "org.apache.commons.compress.archivers.jar", + "org.apache.commons.compress.archivers.sevenz", + "org.apache.commons.compress.archivers.tar", + "org.apache.commons.compress.archivers.zip", + "org.apache.commons.compress.changes", + "org.apache.commons.compress.compressors", + "org.apache.commons.compress.compressors.brotli", + "org.apache.commons.compress.compressors.bzip2", + "org.apache.commons.compress.compressors.deflate", + "org.apache.commons.compress.compressors.deflate64", + "org.apache.commons.compress.compressors.gzip", + "org.apache.commons.compress.compressors.lz4", + "org.apache.commons.compress.compressors.lz77support", + "org.apache.commons.compress.compressors.lzma", + "org.apache.commons.compress.compressors.lzw", + "org.apache.commons.compress.compressors.pack200", + "org.apache.commons.compress.compressors.snappy", + "org.apache.commons.compress.compressors.xz", + "org.apache.commons.compress.compressors.z", + "org.apache.commons.compress.compressors.zstandard", + "org.apache.commons.compress.parallel", + "org.apache.commons.compress.utils" + ], + "org.apache.httpcomponents:httpclient": [ + "org.apache.http.auth", + "org.apache.http.auth.params", + "org.apache.http.client", + "org.apache.http.client.config", + "org.apache.http.client.entity", + "org.apache.http.client.methods", + "org.apache.http.client.params", + "org.apache.http.client.protocol", + "org.apache.http.client.utils", + "org.apache.http.conn", + "org.apache.http.conn.params", + "org.apache.http.conn.routing", + "org.apache.http.conn.scheme", + "org.apache.http.conn.socket", + "org.apache.http.conn.ssl", + "org.apache.http.conn.util", + "org.apache.http.cookie", + "org.apache.http.cookie.params", + "org.apache.http.impl.auth", + "org.apache.http.impl.client", + "org.apache.http.impl.conn", + "org.apache.http.impl.conn.tsccm", + "org.apache.http.impl.cookie", + "org.apache.http.impl.execchain" + ], + "org.apache.httpcomponents:httpcore": [ + "org.apache.http", + "org.apache.http.annotation", + "org.apache.http.concurrent", + "org.apache.http.config", + "org.apache.http.entity", + "org.apache.http.impl", + "org.apache.http.impl.bootstrap", + "org.apache.http.impl.entity", + "org.apache.http.impl.io", + "org.apache.http.impl.pool", + "org.apache.http.io", + "org.apache.http.message", + "org.apache.http.params", + "org.apache.http.pool", + "org.apache.http.protocol", + "org.apache.http.ssl", + "org.apache.http.util" + ], + "org.apache.httpcomponents:httpmime": [ + "org.apache.http.entity.mime", + "org.apache.http.entity.mime.content" + ], + "org.bouncycastle:bcpkix-jdk15on": [ + "org.bouncycastle.cert", + "org.bouncycastle.cert.bc", + "org.bouncycastle.cert.cmp", + "org.bouncycastle.cert.crmf", + "org.bouncycastle.cert.crmf.bc", + "org.bouncycastle.cert.crmf.jcajce", + "org.bouncycastle.cert.dane", + "org.bouncycastle.cert.dane.fetcher", + "org.bouncycastle.cert.jcajce", + "org.bouncycastle.cert.ocsp", + "org.bouncycastle.cert.ocsp.jcajce", + "org.bouncycastle.cert.path", + "org.bouncycastle.cert.path.validations", + "org.bouncycastle.cert.selector", + "org.bouncycastle.cert.selector.jcajce", + "org.bouncycastle.cms", + "org.bouncycastle.cms.bc", + "org.bouncycastle.cms.jcajce", + "org.bouncycastle.dvcs", + "org.bouncycastle.eac", + "org.bouncycastle.eac.jcajce", + "org.bouncycastle.eac.operator", + "org.bouncycastle.eac.operator.jcajce", + "org.bouncycastle.mozilla", + "org.bouncycastle.mozilla.jcajce", + "org.bouncycastle.openssl", + "org.bouncycastle.openssl.bc", + "org.bouncycastle.openssl.jcajce", + "org.bouncycastle.operator", + "org.bouncycastle.operator.bc", + "org.bouncycastle.operator.jcajce", + "org.bouncycastle.pkcs", + "org.bouncycastle.pkcs.bc", + "org.bouncycastle.pkcs.jcajce", + "org.bouncycastle.pkix", + "org.bouncycastle.pkix.jcajce", + "org.bouncycastle.tsp", + "org.bouncycastle.tsp.cms", + "org.bouncycastle.voms" + ], "org.bouncycastle:bcprov-jdk15on": [ "org.bouncycastle", "org.bouncycastle.asn1", @@ -3281,6 +5079,38 @@ "org.eclipse.parsson", "org.eclipse.parsson.api" ], + "org.glassfish.jaxb:jaxb-runtime": [ + "com.sun.xml.bind", + "com.sun.xml.bind.annotation", + "com.sun.xml.bind.api", + "com.sun.xml.bind.api.impl", + "com.sun.xml.bind.marshaller", + "com.sun.xml.bind.unmarshaller", + "com.sun.xml.bind.util", + "com.sun.xml.bind.v2", + "com.sun.xml.bind.v2.bytecode", + "com.sun.xml.bind.v2.model.annotation", + "com.sun.xml.bind.v2.model.core", + "com.sun.xml.bind.v2.model.impl", + "com.sun.xml.bind.v2.model.nav", + "com.sun.xml.bind.v2.model.runtime", + "com.sun.xml.bind.v2.model.util", + "com.sun.xml.bind.v2.runtime", + "com.sun.xml.bind.v2.runtime.output", + "com.sun.xml.bind.v2.runtime.property", + "com.sun.xml.bind.v2.runtime.reflect", + "com.sun.xml.bind.v2.runtime.reflect.opt", + "com.sun.xml.bind.v2.runtime.unmarshaller", + "com.sun.xml.bind.v2.schemagen", + "com.sun.xml.bind.v2.schemagen.episode", + "com.sun.xml.bind.v2.schemagen.xmlschema", + "com.sun.xml.bind.v2.util" + ], + "org.glassfish.jaxb:txw2": [ + "com.sun.xml.txw2", + "com.sun.xml.txw2.annotation", + "com.sun.xml.txw2.output" + ], "org.hamcrest:hamcrest-core": [ "org.hamcrest", "org.hamcrest.core", @@ -4220,6 +6050,10 @@ "org.intellij.lang.annotations", "org.jetbrains.annotations" ], + "org.jvnet.staxex:stax-ex": [ + "org.jvnet.staxex", + "org.jvnet.staxex.util" + ], "org.mockito.kotlin:mockito-kotlin": [ "org.mockito.kotlin", "org.mockito.kotlin.internal" @@ -4388,6 +6222,57 @@ "org.robolectric:utils-reflector": [ "org.robolectric.util.reflector" ], + "org.smali:baksmali": [ + "org.jf.baksmali", + "org.jf.baksmali.Adaptors", + "org.jf.baksmali.Adaptors.Debug", + "org.jf.baksmali.Adaptors.EncodedValue", + "org.jf.baksmali.Adaptors.Format", + "org.jf.baksmali.Renderers" + ], + "org.smali:dexlib2": [ + "org.jf.dexlib2", + "org.jf.dexlib2.analysis", + "org.jf.dexlib2.analysis.reflection", + "org.jf.dexlib2.analysis.reflection.util", + "org.jf.dexlib2.analysis.util", + "org.jf.dexlib2.base", + "org.jf.dexlib2.base.reference", + "org.jf.dexlib2.base.value", + "org.jf.dexlib2.builder", + "org.jf.dexlib2.builder.debug", + "org.jf.dexlib2.builder.instruction", + "org.jf.dexlib2.dexbacked", + "org.jf.dexlib2.dexbacked.instruction", + "org.jf.dexlib2.dexbacked.raw", + "org.jf.dexlib2.dexbacked.raw.util", + "org.jf.dexlib2.dexbacked.reference", + "org.jf.dexlib2.dexbacked.util", + "org.jf.dexlib2.dexbacked.value", + "org.jf.dexlib2.iface", + "org.jf.dexlib2.iface.debug", + "org.jf.dexlib2.iface.instruction", + "org.jf.dexlib2.iface.instruction.formats", + "org.jf.dexlib2.iface.reference", + "org.jf.dexlib2.iface.value", + "org.jf.dexlib2.immutable", + "org.jf.dexlib2.immutable.debug", + "org.jf.dexlib2.immutable.instruction", + "org.jf.dexlib2.immutable.reference", + "org.jf.dexlib2.immutable.util", + "org.jf.dexlib2.immutable.value", + "org.jf.dexlib2.rewriter", + "org.jf.dexlib2.util", + "org.jf.dexlib2.writer", + "org.jf.dexlib2.writer.builder", + "org.jf.dexlib2.writer.io", + "org.jf.dexlib2.writer.pool", + "org.jf.dexlib2.writer.util" + ], + "org.smali:util": [ + "org.jf.util", + "org.jf.util.jcommander" + ], "org.snakeyaml:snakeyaml-engine": [ "org.snakeyaml.engine.external.com.google.gdata.util.common.base", "org.snakeyaml.engine.v2.api", @@ -4410,6 +6295,79 @@ "org.snakeyaml.engine.v2.schema", "org.snakeyaml.engine.v2.serializer", "org.snakeyaml.engine.v2.tokens" + ], + "xerces:xercesImpl": [ + "org.apache.html.dom", + "org.apache.wml", + "org.apache.wml.dom", + "org.apache.xerces.dom", + "org.apache.xerces.dom.events", + "org.apache.xerces.dom3.as", + "org.apache.xerces.impl", + "org.apache.xerces.impl.dtd", + "org.apache.xerces.impl.dtd.models", + "org.apache.xerces.impl.dv", + "org.apache.xerces.impl.dv.dtd", + "org.apache.xerces.impl.dv.util", + "org.apache.xerces.impl.dv.xs", + "org.apache.xerces.impl.io", + "org.apache.xerces.impl.msg", + "org.apache.xerces.impl.validation", + "org.apache.xerces.impl.xpath", + "org.apache.xerces.impl.xpath.regex", + "org.apache.xerces.impl.xs", + "org.apache.xerces.impl.xs.identity", + "org.apache.xerces.impl.xs.models", + "org.apache.xerces.impl.xs.opti", + "org.apache.xerces.impl.xs.traversers", + "org.apache.xerces.impl.xs.util", + "org.apache.xerces.jaxp", + "org.apache.xerces.jaxp.datatype", + "org.apache.xerces.jaxp.validation", + "org.apache.xerces.parsers", + "org.apache.xerces.stax", + "org.apache.xerces.stax.events", + "org.apache.xerces.util", + "org.apache.xerces.xinclude", + "org.apache.xerces.xni", + "org.apache.xerces.xni.grammars", + "org.apache.xerces.xni.parser", + "org.apache.xerces.xpointer", + "org.apache.xerces.xs", + "org.apache.xerces.xs.datatypes", + "org.apache.xml.serialize", + "org.w3c.dom.html" + ], + "xml-apis:xml-apis": [ + "javax.xml", + "javax.xml.datatype", + "javax.xml.namespace", + "javax.xml.parsers", + "javax.xml.stream", + "javax.xml.stream.events", + "javax.xml.stream.util", + "javax.xml.transform", + "javax.xml.transform.dom", + "javax.xml.transform.sax", + "javax.xml.transform.stax", + "javax.xml.transform.stream", + "javax.xml.validation", + "javax.xml.xpath", + "org.apache.xmlcommons", + "org.w3c.dom", + "org.w3c.dom.bootstrap", + "org.w3c.dom.css", + "org.w3c.dom.events", + "org.w3c.dom.html", + "org.w3c.dom.ls", + "org.w3c.dom.ranges", + "org.w3c.dom.stylesheets", + "org.w3c.dom.traversal", + "org.w3c.dom.views", + "org.w3c.dom.xpath", + "org.xml.sax", + "org.xml.sax.ext", + "org.xml.sax.helpers" ] }, "repositories": { @@ -4497,9 +6455,30 @@ "androidx.work:work-testing:aar", "com.almworks.sqlite4java:sqlite4java", "com.android.databinding:baseLibrary", + "com.android.tools.analytics-library:protos", + "com.android.tools.analytics-library:shared", + "com.android.tools.apkparser:apkanalyzer", + "com.android.tools.apkparser:binary-resources", "com.android.tools.build.jetifier:jetifier-core", "com.android.tools.build:aapt2-proto", + "com.android.tools.build:builder-model", + "com.android.tools.build:builder-test-api", + "com.android.tools.build:manifest-merger", + "com.android.tools.ddms:ddmlib", + "com.android.tools.external.com-intellij:intellij-core", + "com.android.tools.external.com-intellij:kotlin-compiler", + "com.android.tools.external.org-jetbrains:uast", + "com.android.tools.layoutlib:layoutlib-api", + "com.android.tools.lint:lint-api", + "com.android.tools.lint:lint-checks", + "com.android.tools.lint:lint-model", "com.android.tools:annotations", + "com.android.tools:common", + "com.android.tools:dvlib", + "com.android.tools:repository", + "com.android.tools:sdk-common", + "com.android.tools:sdklib", + "com.beust:jcommander", "com.crashlytics.sdk.android:answers:aar", "com.crashlytics.sdk.android:beta:aar", "com.crashlytics.sdk.android:crashlytics-core:aar", @@ -4570,6 +6549,7 @@ "com.google.guava:guava", "com.google.guava:listenablefuture", "com.google.j2objc:j2objc-annotations", + "com.google.jimfs:jimfs", "com.google.protobuf:protobuf-java", "com.google.protobuf:protobuf-java-util", "com.google.protobuf:protobuf-javalite", @@ -4590,8 +6570,12 @@ "com.squareup:javapoet", "com.squareup:javawriter", "com.squareup:kotlinpoet", + "com.sun.activation:javax.activation", + "com.sun.istack:istack-commons-runtime", + "com.sun.xml.fastinfoset:FastInfoset", "commons-codec:commons-codec", "commons-io:commons-io", + "commons-logging:commons-logging", "io.fabric.sdk.android:fabric:aar", "io.grpc:grpc-android:aar", "io.grpc:grpc-api", @@ -4602,21 +6586,32 @@ "io.grpc:grpc-stub", "io.perfmark:perfmark-api", "io.xlate:yaml-json", + "jakarta.activation:jakarta.activation-api", "jakarta.json:jakarta.json-api", + "jakarta.xml.bind:jakarta.xml.bind-api", "javax.annotation:javax.annotation-api", "javax.inject:javax.inject", "junit:junit", "net.bytebuddy:byte-buddy", "net.bytebuddy:byte-buddy-agent", + "net.java.dev.jna:jna", + "net.java.dev.jna:jna-platform", "net.ltgt.gradle.incap:incap", "net.sf.kxml:kxml2", "nl.dionsegijn:konfetti:aar", "org.antlr:antlr4", + "org.apache.commons:commons-compress", + "org.apache.httpcomponents:httpclient", + "org.apache.httpcomponents:httpcore", + "org.apache.httpcomponents:httpmime", + "org.bouncycastle:bcpkix-jdk15on", "org.bouncycastle:bcprov-jdk15on", "org.checkerframework:checker-compat-qual", "org.checkerframework:checker-qual", "org.codehaus.mojo:animal-sniffer-annotations", "org.eclipse.parsson:parsson", + "org.glassfish.jaxb:jaxb-runtime", + "org.glassfish.jaxb:txw2", "org.hamcrest:hamcrest-core", "org.hamcrest:hamcrest-integration", "org.hamcrest:hamcrest-library", @@ -4641,6 +6636,7 @@ "org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm", "org.jetbrains.kotlinx:kotlinx-metadata-jvm", "org.jetbrains:annotations", + "org.jvnet.staxex:stax-ex", "org.mockito.kotlin:mockito-kotlin", "org.mockito:mockito-core", "org.objenesis:objenesis", @@ -4660,7 +6656,12 @@ "org.robolectric:shadows-framework", "org.robolectric:utils", "org.robolectric:utils-reflector", - "org.snakeyaml:snakeyaml-engine" + "org.smali:baksmali", + "org.smali:dexlib2", + "org.smali:util", + "org.snakeyaml:snakeyaml-engine", + "xerces:xercesImpl", + "xml-apis:xml-apis" ], "https://repo1.maven.org/maven2/": [ "androidx.activity:activity:aar", @@ -4746,9 +6747,30 @@ "androidx.work:work-testing:aar", "com.almworks.sqlite4java:sqlite4java", "com.android.databinding:baseLibrary", + "com.android.tools.analytics-library:protos", + "com.android.tools.analytics-library:shared", + "com.android.tools.apkparser:apkanalyzer", + "com.android.tools.apkparser:binary-resources", "com.android.tools.build.jetifier:jetifier-core", "com.android.tools.build:aapt2-proto", + "com.android.tools.build:builder-model", + "com.android.tools.build:builder-test-api", + "com.android.tools.build:manifest-merger", + "com.android.tools.ddms:ddmlib", + "com.android.tools.external.com-intellij:intellij-core", + "com.android.tools.external.com-intellij:kotlin-compiler", + "com.android.tools.external.org-jetbrains:uast", + "com.android.tools.layoutlib:layoutlib-api", + "com.android.tools.lint:lint-api", + "com.android.tools.lint:lint-checks", + "com.android.tools.lint:lint-model", "com.android.tools:annotations", + "com.android.tools:common", + "com.android.tools:dvlib", + "com.android.tools:repository", + "com.android.tools:sdk-common", + "com.android.tools:sdklib", + "com.beust:jcommander", "com.crashlytics.sdk.android:answers:aar", "com.crashlytics.sdk.android:beta:aar", "com.crashlytics.sdk.android:crashlytics-core:aar", @@ -4819,6 +6841,7 @@ "com.google.guava:guava", "com.google.guava:listenablefuture", "com.google.j2objc:j2objc-annotations", + "com.google.jimfs:jimfs", "com.google.protobuf:protobuf-java", "com.google.protobuf:protobuf-java-util", "com.google.protobuf:protobuf-javalite", @@ -4839,8 +6862,12 @@ "com.squareup:javapoet", "com.squareup:javawriter", "com.squareup:kotlinpoet", + "com.sun.activation:javax.activation", + "com.sun.istack:istack-commons-runtime", + "com.sun.xml.fastinfoset:FastInfoset", "commons-codec:commons-codec", "commons-io:commons-io", + "commons-logging:commons-logging", "io.fabric.sdk.android:fabric:aar", "io.grpc:grpc-android:aar", "io.grpc:grpc-api", @@ -4851,21 +6878,32 @@ "io.grpc:grpc-stub", "io.perfmark:perfmark-api", "io.xlate:yaml-json", + "jakarta.activation:jakarta.activation-api", "jakarta.json:jakarta.json-api", + "jakarta.xml.bind:jakarta.xml.bind-api", "javax.annotation:javax.annotation-api", "javax.inject:javax.inject", "junit:junit", "net.bytebuddy:byte-buddy", "net.bytebuddy:byte-buddy-agent", + "net.java.dev.jna:jna", + "net.java.dev.jna:jna-platform", "net.ltgt.gradle.incap:incap", "net.sf.kxml:kxml2", "nl.dionsegijn:konfetti:aar", "org.antlr:antlr4", + "org.apache.commons:commons-compress", + "org.apache.httpcomponents:httpclient", + "org.apache.httpcomponents:httpcore", + "org.apache.httpcomponents:httpmime", + "org.bouncycastle:bcpkix-jdk15on", "org.bouncycastle:bcprov-jdk15on", "org.checkerframework:checker-compat-qual", "org.checkerframework:checker-qual", "org.codehaus.mojo:animal-sniffer-annotations", "org.eclipse.parsson:parsson", + "org.glassfish.jaxb:jaxb-runtime", + "org.glassfish.jaxb:txw2", "org.hamcrest:hamcrest-core", "org.hamcrest:hamcrest-integration", "org.hamcrest:hamcrest-library", @@ -4890,6 +6928,7 @@ "org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm", "org.jetbrains.kotlinx:kotlinx-metadata-jvm", "org.jetbrains:annotations", + "org.jvnet.staxex:stax-ex", "org.mockito.kotlin:mockito-kotlin", "org.mockito:mockito-core", "org.objenesis:objenesis", @@ -4909,7 +6948,12 @@ "org.robolectric:shadows-framework", "org.robolectric:utils", "org.robolectric:utils-reflector", - "org.snakeyaml:snakeyaml-engine" + "org.smali:baksmali", + "org.smali:dexlib2", + "org.smali:util", + "org.snakeyaml:snakeyaml-engine", + "xerces:xercesImpl", + "xml-apis:xml-apis" ], "https://oss.sonatype.org/content/repositories/snapshots/": [ "androidx.activity:activity:aar", @@ -4995,9 +7039,30 @@ "androidx.work:work-testing:aar", "com.almworks.sqlite4java:sqlite4java", "com.android.databinding:baseLibrary", + "com.android.tools.analytics-library:protos", + "com.android.tools.analytics-library:shared", + "com.android.tools.apkparser:apkanalyzer", + "com.android.tools.apkparser:binary-resources", "com.android.tools.build.jetifier:jetifier-core", "com.android.tools.build:aapt2-proto", + "com.android.tools.build:builder-model", + "com.android.tools.build:builder-test-api", + "com.android.tools.build:manifest-merger", + "com.android.tools.ddms:ddmlib", + "com.android.tools.external.com-intellij:intellij-core", + "com.android.tools.external.com-intellij:kotlin-compiler", + "com.android.tools.external.org-jetbrains:uast", + "com.android.tools.layoutlib:layoutlib-api", + "com.android.tools.lint:lint-api", + "com.android.tools.lint:lint-checks", + "com.android.tools.lint:lint-model", "com.android.tools:annotations", + "com.android.tools:common", + "com.android.tools:dvlib", + "com.android.tools:repository", + "com.android.tools:sdk-common", + "com.android.tools:sdklib", + "com.beust:jcommander", "com.crashlytics.sdk.android:answers:aar", "com.crashlytics.sdk.android:beta:aar", "com.crashlytics.sdk.android:crashlytics-core:aar", @@ -5068,6 +7133,7 @@ "com.google.guava:guava", "com.google.guava:listenablefuture", "com.google.j2objc:j2objc-annotations", + "com.google.jimfs:jimfs", "com.google.protobuf:protobuf-java", "com.google.protobuf:protobuf-java-util", "com.google.protobuf:protobuf-javalite", @@ -5088,8 +7154,12 @@ "com.squareup:javapoet", "com.squareup:javawriter", "com.squareup:kotlinpoet", + "com.sun.activation:javax.activation", + "com.sun.istack:istack-commons-runtime", + "com.sun.xml.fastinfoset:FastInfoset", "commons-codec:commons-codec", "commons-io:commons-io", + "commons-logging:commons-logging", "io.fabric.sdk.android:fabric:aar", "io.grpc:grpc-android:aar", "io.grpc:grpc-api", @@ -5100,21 +7170,32 @@ "io.grpc:grpc-stub", "io.perfmark:perfmark-api", "io.xlate:yaml-json", + "jakarta.activation:jakarta.activation-api", "jakarta.json:jakarta.json-api", + "jakarta.xml.bind:jakarta.xml.bind-api", "javax.annotation:javax.annotation-api", "javax.inject:javax.inject", "junit:junit", "net.bytebuddy:byte-buddy", "net.bytebuddy:byte-buddy-agent", + "net.java.dev.jna:jna", + "net.java.dev.jna:jna-platform", "net.ltgt.gradle.incap:incap", "net.sf.kxml:kxml2", "nl.dionsegijn:konfetti:aar", "org.antlr:antlr4", + "org.apache.commons:commons-compress", + "org.apache.httpcomponents:httpclient", + "org.apache.httpcomponents:httpcore", + "org.apache.httpcomponents:httpmime", + "org.bouncycastle:bcpkix-jdk15on", "org.bouncycastle:bcprov-jdk15on", "org.checkerframework:checker-compat-qual", "org.checkerframework:checker-qual", "org.codehaus.mojo:animal-sniffer-annotations", "org.eclipse.parsson:parsson", + "org.glassfish.jaxb:jaxb-runtime", + "org.glassfish.jaxb:txw2", "org.hamcrest:hamcrest-core", "org.hamcrest:hamcrest-integration", "org.hamcrest:hamcrest-library", @@ -5139,6 +7220,7 @@ "org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm", "org.jetbrains.kotlinx:kotlinx-metadata-jvm", "org.jetbrains:annotations", + "org.jvnet.staxex:stax-ex", "org.mockito.kotlin:mockito-kotlin", "org.mockito:mockito-core", "org.objenesis:objenesis", @@ -5158,7 +7240,12 @@ "org.robolectric:shadows-framework", "org.robolectric:utils", "org.robolectric:utils-reflector", - "org.snakeyaml:snakeyaml-engine" + "org.smali:baksmali", + "org.smali:dexlib2", + "org.smali:util", + "org.snakeyaml:snakeyaml-engine", + "xerces:xercesImpl", + "xml-apis:xml-apis" ], "https://maven.fabric.io/public/": [ "androidx.activity:activity:aar", @@ -5244,9 +7331,30 @@ "androidx.work:work-testing:aar", "com.almworks.sqlite4java:sqlite4java", "com.android.databinding:baseLibrary", + "com.android.tools.analytics-library:protos", + "com.android.tools.analytics-library:shared", + "com.android.tools.apkparser:apkanalyzer", + "com.android.tools.apkparser:binary-resources", "com.android.tools.build.jetifier:jetifier-core", "com.android.tools.build:aapt2-proto", + "com.android.tools.build:builder-model", + "com.android.tools.build:builder-test-api", + "com.android.tools.build:manifest-merger", + "com.android.tools.ddms:ddmlib", + "com.android.tools.external.com-intellij:intellij-core", + "com.android.tools.external.com-intellij:kotlin-compiler", + "com.android.tools.external.org-jetbrains:uast", + "com.android.tools.layoutlib:layoutlib-api", + "com.android.tools.lint:lint-api", + "com.android.tools.lint:lint-checks", + "com.android.tools.lint:lint-model", "com.android.tools:annotations", + "com.android.tools:common", + "com.android.tools:dvlib", + "com.android.tools:repository", + "com.android.tools:sdk-common", + "com.android.tools:sdklib", + "com.beust:jcommander", "com.crashlytics.sdk.android:answers:aar", "com.crashlytics.sdk.android:beta:aar", "com.crashlytics.sdk.android:crashlytics-core:aar", @@ -5317,6 +7425,7 @@ "com.google.guava:guava", "com.google.guava:listenablefuture", "com.google.j2objc:j2objc-annotations", + "com.google.jimfs:jimfs", "com.google.protobuf:protobuf-java", "com.google.protobuf:protobuf-java-util", "com.google.protobuf:protobuf-javalite", @@ -5337,8 +7446,12 @@ "com.squareup:javapoet", "com.squareup:javawriter", "com.squareup:kotlinpoet", + "com.sun.activation:javax.activation", + "com.sun.istack:istack-commons-runtime", + "com.sun.xml.fastinfoset:FastInfoset", "commons-codec:commons-codec", "commons-io:commons-io", + "commons-logging:commons-logging", "io.fabric.sdk.android:fabric:aar", "io.grpc:grpc-android:aar", "io.grpc:grpc-api", @@ -5349,21 +7462,32 @@ "io.grpc:grpc-stub", "io.perfmark:perfmark-api", "io.xlate:yaml-json", + "jakarta.activation:jakarta.activation-api", "jakarta.json:jakarta.json-api", + "jakarta.xml.bind:jakarta.xml.bind-api", "javax.annotation:javax.annotation-api", "javax.inject:javax.inject", "junit:junit", "net.bytebuddy:byte-buddy", "net.bytebuddy:byte-buddy-agent", + "net.java.dev.jna:jna", + "net.java.dev.jna:jna-platform", "net.ltgt.gradle.incap:incap", "net.sf.kxml:kxml2", "nl.dionsegijn:konfetti:aar", "org.antlr:antlr4", + "org.apache.commons:commons-compress", + "org.apache.httpcomponents:httpclient", + "org.apache.httpcomponents:httpcore", + "org.apache.httpcomponents:httpmime", + "org.bouncycastle:bcpkix-jdk15on", "org.bouncycastle:bcprov-jdk15on", "org.checkerframework:checker-compat-qual", "org.checkerframework:checker-qual", "org.codehaus.mojo:animal-sniffer-annotations", "org.eclipse.parsson:parsson", + "org.glassfish.jaxb:jaxb-runtime", + "org.glassfish.jaxb:txw2", "org.hamcrest:hamcrest-core", "org.hamcrest:hamcrest-integration", "org.hamcrest:hamcrest-library", @@ -5388,6 +7512,7 @@ "org.jetbrains.kotlinx:kotlinx-coroutines-test-jvm", "org.jetbrains.kotlinx:kotlinx-metadata-jvm", "org.jetbrains:annotations", + "org.jvnet.staxex:stax-ex", "org.mockito.kotlin:mockito-kotlin", "org.mockito:mockito-core", "org.objenesis:objenesis", @@ -5407,7 +7532,12 @@ "org.robolectric:shadows-framework", "org.robolectric:utils", "org.robolectric:utils-reflector", - "org.snakeyaml:snakeyaml-engine" + "org.smali:baksmali", + "org.smali:dexlib2", + "org.smali:util", + "org.snakeyaml:snakeyaml-engine", + "xerces:xercesImpl", + "xml-apis:xml-apis" ] }, "version": "2" diff --git a/third_party/versions.bzl b/third_party/versions.bzl index c1dec75774d..a90a2d476c6 100644 --- a/third_party/versions.bzl +++ b/third_party/versions.bzl @@ -99,6 +99,7 @@ MAVEN_TEST_DEPENDENCY_VERSIONS = { "androidx.test:core": "1.0.0", "androidx.test:runner": "1.2.0", "androidx.work:work-testing": "2.4.0", + "com.android.tools.apkparser:apkanalyzer": "30.0.4", "com.github.bumptech.glide:mocks": "4.11.0", "com.google.protobuf:protobuf-java": "3.17.3", "com.google.protobuf:protobuf-java-util": "3.17.3", @@ -135,6 +136,10 @@ HTTP_DEPENDENCY_VERSIONS = { "sha": "9425a423a4cb9d9db0356300722d9bd8e634cf539f29d97bb84f457cccd16eb8", "version": "31.0.1", }, + "guava_jre": { + "sha": "d5be94d65e87bd219fb3193ad1517baa55a3b88fc91d21cf735826ab5af087b9", + "version": "31.0.1", + }, "kotlinx-coroutines-core-jvm": { "sha": "acc8c74b1fb88121c51221bfa7b6f5e920201bc20183ebf74165dcf5d45a8003", "version": "1.6.0",