Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #4119, #4120, and part of #59: Upgrade to Kotlin 1.6.10 #4937

Merged
merged 193 commits into from
Jun 12, 2024

Conversation

BenHenning
Copy link
Member

@BenHenning BenHenning commented Apr 1, 2023

Explanation

Fixes #4119
Fixes #4120
Fixes part of #59

This PR finishes the migration of the codebase to Kotlin 1.6 (addressing both #4119 and #4120).

Kotlin 1.6 is needed as part of moving rules_kotlin to 1.7.x (which is, in turn, needed in conjunction with Bazel 6.x to enable strict dependency checking which significantly simplifies modularization which is planned for downstream PRs). This PR doesn't actually finish the movement to that version of rules_kotlin, but it does finish moving the codebase to a new enough (and no longer pre-release) version of rules_kotlin to allow using Kotlin 1.6 (over Kotlin 1.4 that the codebase currently uses): version 1.5.0.

Previous PRs (#5400 and #5402) prepared for the changes here by addressing large categories of build warnings that have either arisen from this migration, or from past work. Note that another large category of warnings have also been addressed in this PR: by moving to Kotlin 1.6, there's no longer a runtime incompatibility between the Kotlin SDK and the reflection APIs (which was causing a lot of warning output previously). Between all three PRs, the output is now very clean and free of nearly all build warnings.

To try and keep the warnings clean long-term, this PR introduces warnings-as-errors for both Java and Kotlin code. However, please note some caveats:

  • Dagger generated code doesn't follow the Java warnings-as-error flag, so those warnings were cleaned up manually (and will need to be generally watched for, unfortunately).
  • The version of rules_kotlin used in this PR doesn't directly support turning on the functionality, but does internally (so a small patch file has been added to augment rules_kotlin). When the codebase is updated to rules_kotlin 1.7.x this patch will no longer be needed.
  • To ease development, a build configuration flag was added to disable failure upon encountering build warnings (per https://bazel.build/run/bazelrc and https://bazel.build/docs/configurable-attributes#query-and-cquery as an example), though this needs to be opted into:
    bazel build --config=ignore_build_warnings <target>

Some other details to note:

  • Version 1.6.10 is specifically picked in order to ensure Jetpack Compose compatibility (for preparation of the work being prototyped in Prototype Compose on Bazel  #5401 to be compatible with the Oppia Android build environment).
  • The vast majority of code in this PR is updating parameterized tests to use a cleaner repeatable annotation pattern that wasn't available in Kotlin 1.4.
  • This upgrade absolutely does have runtime implications, but we're relying very heavily on existing automated tests to ensure correctness and no regressions.
  • This PR doesn't make an effort to move toward newer Kotlin language features except where forced (API deprecations) or largely wanted (the repeatable annotation change).
  • android-spotlight and kotlitex have been updated to support newer versions of Kotlin (as both are custom forks managed in the broader Oppia GitHub organization).
  • Gradle files have been updated to match the same dependency versions as Bazel (where it was obvious to make changes; some might still be a bit off).
  • The Gradle build configuration was also updated to use Kotlin 1.6.x (otherwise there would be build incompatibilities with Bazel). I think this is the last upgrade we can do for Gradle without upgrading AGP (which will cause us significant issues with the model module, so we're planning on instead dropping Gradle support).
  • API changes that needed to be addressed in this PR due to deprecations include: String.captialize, String.toLowerCase, String.toUpperCase, SendChannel.offer, and Char.toInt.
  • New API changes that have been leveraged in this PR: Flow.lastOrNull and Deferred.asListenableFuture (to replace SettableFuture for safety; this also resulted in nice simplifications in CoroutineExecutorService).
  • The JVM coroutines dependency needed to be split out from Maven and manually imported with some empty internal Java class files since it otherwise has some issues being desugared: Bazel 4.1 fails to desugar kotlinx-coroutines-core-jvm:1.5.0 bazelbuild/bazel#13553. This is a problem with the Desugarer used in Bazel 4.x (and maybe later versions, so this solution will probably need to kept for a while).
  • Some Proguard rule updates were needed due to Kotlin SDK changes--see the Proguard file & comments for specifics.
  • Due to dependency changes, the KitKat main dex file was also trimmed down. I'm fairly certain that it's already crashing on startup, so I don't care much about this change--it just needs to build. We plan to remove KitKat entirely eventually, anyway: Deprecate support for Android KitKat #5012.
  • Jetifier (that is, automatic conversion from support libraries to Jetpack/AndroidX) support was disabled in Gradle. We don't have it enabled in Bazel, and it could potentially encourage strange one-version violations if it was ever actually needed. This is a safer (and likely more performant) change to make.
  • Moshi was updated to 1.13 to support the upgrade in Kotlin. This did result in a small configuration change due to its annotation processor being moved. Note that Moshi 1.14 couldn't be supported since it requires Kotlin 1.7+ which requires rules_kotlin 1.7+. This will be an option to upgrade in the future.
  • Some improvements and fixes were made in FilterPerLanguageResources (I think it was outputting something incorrectly before and that's now been fixed as part of a broader logical reworking of the filtering logic).
  • com.android.support:support-annotation was removed as a dependency since it was never used in Bazel, and shouldn't be used (since it's support library and not AndroidX).
  • The updates to Moshi and Kotlin dependencies resulted in a bunch of other transitive dependency updates.
  • Note that Gradle doesn't have allWarningsAsErrors enabled since it would require fixing more warnings than is exposed in Bazel, and we're using Bazel builds as the general source of truth for code quality.

Essential Checklist

  • The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
  • Any changes to scripts/assets files have their rationale included in the PR explanation.
  • The PR follows the style guide.
  • The PR does not contain any unnecessary code changes from Android Studio (reference).
  • The PR is made from a branch that's not called "develop" and is up-to-date with "develop".
  • The PR is assigned to the appropriate reviewers (reference).

For UI-specific PRs only

N/A -- This is an infrastructural change. While it could inadvertently affect user-facing code, it shouldn't based on the current passing state of automated tests.

The check & related scripts required fairly involved reworking since the
Maven install file (from which it sources Maven URL context) changed in
format as part of the upgrade for rules_jvm_external. This has actually
led to what seems to be more correct analysis of libraries that the
build depends on, so more licenses have been added to the
maven_dependencies.textproto tracking file.

One unused Crashlytics dependency was removed since it was referencing
an old license that doesn't exist anymore (and the artifact should be
replaced in full by more recent Firebase Crashlytics dependencies that
we are already using).
This addresses an underlying bug with the command executor that can, in
some cases, break compute_affected_tests. It also refines some of its
internal mechanisms for much better performance on expensive PRs.

It also prepares the base support needed for merge queues, but the
CI workflows aren't being updated in this change.
This prepares for merge queues (but doesn't quite configure the workflow
for them--that will happen in a different PR), and improves how tests
are computed for stacked PRs.
…xternal

Conflicts:
	scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel
	scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesListCheck.kt
	scripts/src/java/org/oppia/android/scripts/license/MavenDependenciesRetriever.kt
	scripts/src/java/org/oppia/android/scripts/maven/GenerateMavenDependenciesList.kt
	scripts/src/javatests/org/oppia/android/scripts/license/MavenDependenciesListCheckTest.kt
	scripts/src/javatests/org/oppia/android/scripts/license/MavenDependenciesRetrieverTest.kt
	scripts/src/javatests/org/oppia/android/scripts/maven/GenerateMavenDependenciesListTest.kt
Also, update TODO check script to have nicer output, and support
generating the exemption textproto file for easier updates in the
future.
This moves the codebase to using the recommended single top-level Dagger
library rather than replicating it in a bunch of different places.
This is needed for downstream work. It also includes ensuring that Guava
JRE can never be used (since only Android should ever be referenced by
the production app build).
There's some cleanup work needed beyond this, but this is the core
change to introduce Kotlin 1.6 support (at least for dev builds).

Moshi needed to be upgraded due to a metadata incompatibility when moving
over to Kotlin 1.6.
rules_kotlin moved its imports into more structured bzl files to load,
so this updates all references in the codebase to point to the correct
locations (which removes debug warnings that show up on the CLI).
Moshi 1.14 pulls in kotlin-reflect 1.7.0 which isn't compatible with the
rules_kotlin version we need for Bazel 4.x. Relatedly, this downgrades
rules_kotlin to 1.5.0, but it fortunately keeps all other changes needed
for 1.7.1 (which will be used in a later PR).

Some code fixes were needed, too, for unknown reasons (since the build
should've been using Kotlin 1.6 before). Either way, these fixes seem
reasonable.
Fixed all warnings that the compiler warned about.

Removed ViewModelProvider & fixed state leaking entirely by moving away
from Jetpack's ViewModel as a base class (since we aren't correctly
using that correctly).
Enables Java warnings-as-errors, though this doesn't yet apply to
kapt-generated code (such as the code from Dagger), but those warnings
were still manually fixed.

This also fixes a small import warning in a proto file, and warnings
when building oppia_dev_kitkat (by updating the main dex list, but it's
likely the build doesn't work anymore, anyway, and it's hard to test
locally).
There's a race condition when building large numbers of app tests
simultaneously that can lead to build failures. While the CI runs are
resilient now to this failure, it'd be better to try and fix it. This
removes the last non-AndroidX dependency from the codebase with hopes
that it helps reduce the likelihood of the error (though there are no
dependencies on it, so it's unlikely).
.bazelrc Show resolved Hide resolved
tools/kotlin/BUILD.bazel Outdated Show resolved Hide resolved
Specifically:
- The TODO check was fixed by reformatting a TODO comment.
- The Proguard build was fixed by upgrading kotlinx.coroutines to use a
  version compatible with Kotlin 1.6, as well as adding a Proguard
  dontwarn directive for one class that can't execute on Android.
…tlin1.6

Conflicts:
	domain/BUILD.bazel
	third_party/versions.bzl
Copy link
Member Author

@BenHenning BenHenning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulled in upstream changes and self-reviewed. No concerns at the moment.

Copy link
Collaborator

@adhiamboperes adhiamboperes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @BenHenning!

Apart from a few clarifications and a nit, everything looks good.

I did also wonder about the comment regarding FilterPerLanguageResources. If the changes fixed an error output, is it testable?

Base automatically changed from fix-build-time-kotlin-java-warnings to develop June 3, 2024 20:44
BenHenning added a commit that referenced this pull request Jun 3, 2024
…5402)

## Explanation
Fixes part of #4120
Fixes part of #1051

Similar to #5400, this brings forward changes that would otherwise go in
#4937 to simplify the transition to Kotlin 1.6.

Part of #4937 is introducing warnings-as-errors for both Kotlin and Java
in order to reduce developer error, simplify the codebase, and minimize
warnings when building (which can result in developer habits of ignoring
warnings that might have real consequences to end users of the app). In
order to keep the main migration PR smaller, this PR fixes all existing
warnings and any new ones detected with the Kotlin 1.6 compiler that are
not tied to Kotlin 1.5/1.6 API changes (those are part of #4937,
instead). Fortunately, most of the changes could be brought forward into
this PR.

Specific things to note:
- A few new issues were filed for SDK 33 deprecations caused, but not
noted by, #5222): #5404, #5405, and #5406 and corresponding TODOs added.
This PR opts for TODOs over actual fixes to minimize the amount of
manual verification needed, and to try and keep the PR more focused on
non-functional refactor changes (to reduce the risk as reverting this PR
may be difficult if an issue is introduced).
- A lot of the fixes were removing redundant casts or null checks.
- The old mechanism we used for view models is deprecated, and had a lot
of problems (partially documented in #1051). This PR moves the codebase
over to directly injecting view models instead of using the view model
provider (thus getting rid of true Jetpack view models entirely in the
codebase).
- We never used the Jetpack functionality, and we were leaking a lot of
context objects that could theoretically result in memory leaks.
- The migration of view models in this way has already been ongoing in
the codebase; this PR just finishes moving the rest of them over to
remove the deprecated JetPack view model reference.
- Note that this doesn't actually change the scope of the view models,
and in fact they should largely behave as they always have.
- ``ObservableViewModel`` was subsequently updated, and may be something
we could remove in the future now that it's no longer a Jetpack view
model.
- The old view model binding code was removed, along with its test file
exemptions. It's no longer used now that the view models have been
finished being migrated over to direct injection.
- Some of the binding adapters didn't correctly correspond to their
namespaced properties. I _think_ that the databinding compiler was still
hooking them up correctly, but they produced build warnings that have
now been addressed (specifically, 'app' is implied). Some other
properties were using unusual namespaces, so these were replaced with
'app' versions for consistency & correctness.
- Some cases where SAM interfaces could be converted to lambdas were
also addressed (mainly for ``Observer`` callbacks in UI code).
- ``DrawerLayout.setDrawerListener`` was replaced with calls to
``DrawerLayout.addDrawerListener`` since the former [is
deprecated](https://developer.android.com/reference/androidx/drawerlayout/widget/DrawerLayout#setDrawerListener(androidx.drawerlayout.widget.DrawerLayout.DrawerListener)).
This isn't expected to have a functional difference.
- Some other minor control flow warnings were addressed (such as dead
code paths).
- ``when`` cases were updated to be comprehensive (as this is enforced
starting in newer versions of Kotlin even for non-result based ``when``
statements).
- Some unused variables were removed and/or replaced with ``_`` per
Kotlin convention.
- Some parameter names needed to be updated to match their override
signatures.
- One change in ``ExitSurveyConfirmationDialogFragment`` involved
removing parsing a profile ID. Technically this is a semantic change
since now a crash isn't going to happen if the profile ID is missing or
incorrect, but that seems fine since the fragment doesn't even need a
profile ID to be passed.
- Some of the test activities were updated to bind a ``Runnable``
callback rather than binding to a method (just to avoid passing the
unused ``View`` parameter and to keep things a bit simple binding-wise).
- Some cases were fixed where variables were being shadowed due to
reused names in deeper scopes.
- There were some typing issues going on in tests with custom test
application components. This has been fixed by explicitly declaring the
application component types rather than them being implicit within the
generated Dagger code.
- ``getDrawable`` calls were updated to pass in a ``Theme`` since the
non-theme version is deprecated.
- Some Java property references were updated, too (i.e. using property
syntax instead of Java getters when referencing Java code in Kotlin).
- In some cases, deprecated APIs were suppressed since they're needed
for testing purposes.
- Mockito's ``verifyZeroInteractions`` has been deprecated in favor of
``verifyNoMoreInteractions``, so updates were made in tests accordingly.
- ``ExperimentalCoroutinesApi`` and ``InternalCoroutinesApi`` have been
deprecated in favor of a newer ``OptIn`` method (which can actually be
done via kotlinc arguments, but not in this PR). Thus, they've been
outright removed in cases where not needed, and otherwise migrated to
the ``OptIn`` approach where they do need to be declared.
- In some cases, Kotlin recommends using a ``toSet()`` conversion for
iterable operations when it's more performant, so some of those cases
(where noticed) have been addressed.
- Some unused parameter cases needed to be suppressed for situations
when Robolectric is using reflection to access them.
- In some cases Android Studio would recommend transformation chain
simplifications; these were adopted where obvious.
- There are a few new TODOs added on #3616 as well, to clean up
deprecated references that have been suppressed in this PR.
- ``BundleExtensions`` was updated to implement its own version of the
type-based ``getSerializable`` until such time as ``BundleCompat`` can
be used, instead (per #5405).
- A **lot** of nullability improvements needed to happen throughout the
JSON asset loading process since there was a lot of loose typing
happening there.
- Some Kotlin & OkHttp deprecated API references were also updated to
use their non-deprecated replacements.
- ``NetworkLoggingInterceptorTest`` was majorly reworked to ensure that
the assertions would actually run (``runBlockingTest`` was being used
which is deprecated, and something I try to avoid since it's very
difficult to write tests that use it correctly). My investigations
showed that the assertions weren't being called, so these tests would
never fail. The new versions will always run the assertions or fail
before reaching them, and fortunately the code under test passes the
assertions correctly. Ditto for ``ConsoleLoggerTest``.
- Some parts of ``SurveyProgressController`` were reworked to have
better typing management and to reduce the need for nullability
management.
- Some generic typing trickiness needed to be fixed ahead of the Kotlin
version upgrade in ``UrlImageParser``. See file comments & links in
those comments for more context.
- ``BundleExtensionsTest`` had to be changed since
``getSerializableExtra`` is now deprecated. We also can't update the
test to run SDK 33 since that requires upgrading Robolectric, and
Robolectric can't be upgraded without upgrading other dependencies that
eventually lead to needing to upgrade both Kotlin and Bazel (so it's a
non-starter; this is a workaround until we can actually move to a newer
version of Robolectric).
- There was some minor code-deduplication & cleanup done in
``ClickableAreasImage``.
- Some incorrect comments were removed in tests (to the effect of "using
not-allowed-listed variables should result in a failure."). These seemed
to have been copied from an earlier test, but the later tests weren't
actually verifying that behavior so the comment wasn't correct.
- An unused method was removed from ``ConceptCardRetriever``
(``createWrittenTranslationFromJson``) and some other small
cleanup/consolidation work happened in that class.
- Some stylistic changes were done in ``TopicController`` for JSON
loading to better manage nullable situations, and to try and make the
JSON loading code slightly more Kotlin idiomatic.

Note that overall the PR has relied **heavily** on tooling to detect
warnings to fix, and automated tests to verify that the changes have no
side effects.

Note also that this PR does not actually enable warnings-as-errors; that
will happen in a downstream PR.

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If
this PR fixes part of an issue, prefix the title with "Fix part of
#bugnum: ...".)
- [x] Any changes to
[scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets)
files have their rationale included in the PR explanation.
- [x] The PR follows the [style
guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android
Studio
([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and
is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers
([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- While this changes UI code, it should change very few UI
behaviors and only failure cases for those it does affect. It's largely
infrastructural-only and falls mainly under refactoring/cleanup work.

---------

Co-authored-by: Adhiambo Peres <[email protected]>
Co-authored-by: Sean Lip <[email protected]>
Copy link

oppiabot bot commented Jun 10, 2024

Hi @BenHenning, I'm going to mark this PR as stale because it hasn't had any updates for 7 days. If no further activity occurs within 7 days, it will be automatically closed so that others can take up the issue.
If you are still working on this PR, please make a follow-up commit within 3 days (and submit it for review, if applicable). Please also let us know if you are stuck so we can help you!

@oppiabot oppiabot bot added the stale Corresponds to items that haven't seen a recent update and may be automatically closed. label Jun 10, 2024
Add a config to allow warnings-as-errors to be disabled for local
development to make things easier. The default is still to treat them as
errors (and that will be the build configuration for CI, so PRs
shouldn't be mergeable with Kotlin build warnings).
@oppiabot oppiabot bot removed the stale Corresponds to items that haven't seen a recent update and may be automatically closed. label Jun 11, 2024
Copy link
Member Author

@BenHenning BenHenning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-reviewed latest changes.

@BenHenning
Copy link
Member Author

Thanks @BenHenning!

Apart from a few clarifications and a nit, everything looks good.

I did also wonder about the comment regarding FilterPerLanguageResources. If the changes fixed an error output, is it testable?

Thanks @adhiamboperes! I've addressed my earlier + your comments in the latest changes, and brought the PR up-to-date with develop. As part of this, I also updated the PR description to explain how to use the new opt-in build flag for disabling warnigns-as-errors. PTAL.

Regarding FilterPerLanguageResources, I went back to look at the changes and tests. It seems to me that the existing test coverage seems pretty good for verifying that the changes still work as expected (and particularly testUtility_whenResourcesAreRemoved_printsDiagnosticInformation). The exact failures that were originally fixed I have, unfortunately, forgotten as this change was made over a year ago and has kind of sat while the rest of the Bazel chain was being worked on. I think the important takeaway is that it's functionally still working. If you think additional testing is important here, could you please let me know where you think the functional gap is? I'm always happy to add more tests.

@BenHenning BenHenning changed the title Fix #4119, #4120, and part of #59: Upgrade to Kotlin 1.6.10 [Blocked on #5402] Fix #4119, #4120, and part of #59: Upgrade to Kotlin 1.6.10 Jun 11, 2024
Copy link
Member Author

@BenHenning BenHenning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-reviewed lint fix.

Copy link
Collaborator

@adhiamboperes adhiamboperes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @BenHenning! Everything looks good. I'm happy to leave the filter language resources as is.

@adhiamboperes
Copy link
Collaborator

Merging since everything is up to date.

@adhiamboperes adhiamboperes merged commit 4c7f811 into develop Jun 12, 2024
43 checks passed
@adhiamboperes adhiamboperes deleted the upgrade-to-kotlin1.6 branch June 12, 2024 08:51
@adhiamboperes
Copy link
Collaborator

1 test is flaky:

org.oppia.android.data.backends.gae.NetworkLoggingInterceptorTest > testLoggingInterceptor_makeNetworkCall_succeeds FAILED
    java.lang.IllegalStateException: This job has not completed yet
        at kotlinx.coroutines.JobSupport.getCompletedInternal$kotlinx_coroutines_core(JobSupport.kt:1199)
        at kotlinx.coroutines.DeferredCoroutine.getCompleted(Builders.common.kt:100)
        at org.oppia.android.data.backends.gae.NetworkLoggingInterceptorTest.testLoggingInterceptor_makeNetworkCall_succeeds(NetworkLoggingInterceptorTest.kt:99)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Migrate codebase to Kotlin 1.6 Migrate codebase to Kotlin 1.5
3 participants