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

Improve jdeps logic to find more explicit and implicit dependencies #1118

Merged
merged 11 commits into from
Mar 4, 2024

Conversation

scosenza
Copy link
Contributor

@scosenza scosenza commented Feb 26, 2024

Problem

Kotlin jdeps do not collect many direct and indirect dependencies that are required for compilation. These missing jdep found dependencies were discovered as Airbnb tests a "jdeps compile-time pruning" optimization which provides significant IntelliJ indexing, UI, and compilation performance benefits in our fork of the Bazel IntelliJ Plugin. See also #1115 as @Bencodes and I unknowingly started looking at this issue at about the same time!

Solution

As a followup to #1045, write test cases to repro missing deps found when compiling Airbnb's JVM monorepo, and then fix the underlying issues. Note that these fixes were made with very minimal knowledge of Kotlin compiler internals, so please suggest alternatives if there are better ways to collect these types. Thanks!

Test Plan:

  • Wrote Kotlin and Java test cases to repro missing dependencies found in the Airbnb monorepo
  • Updated Airbnb's minimal rules_kotlin fork to use the changes in this PR and the updated rules_kotlin fork has been successfully running in CI for over a week.

Appendix

For anyone interested in how we're planning to use jdeps pruning:

  • We've been experimenting with using IntelliJ's incremental compiler to perform compilations after the initial Bazel sync.
  • We use jdeps to prune the compile classpath, so that only the interface jars needed to compile the editable targets are added as IntelliJ libraries. For one of our large services, we see a 20x reduction in compile time jars (2000 -> 100). This reduction speeds up indexing, UI responsiveness, and incremental compilation speed (5s -> 1s).
  • Using IntelliJ's incremental compiler with a jdeps pruned classpath has been 30-70x faster than "bazel build/test" for the same large service (30-70s -> 1s) on an M2 Mac. Note that we follow "1:1:1" and have a BUILD file in every Java/Kotlin/Scala package.

…ncies (bazelbuild#17)

# Problem
Kotlin jdeps do not collect many direct and indirect dependencies that are required for compilation. These missing jdep found dependencies were discovered as Airbnb tests a "jdeps compile-time pruning" optimization which provides significant IntelliJ indexing, UI, and compilation performance benefits in our fork of the [Bazel IntelliJ Plugin](https://github.com/bazelbuild/intellij).

# Solution
As a followup to bazelbuild#1045, write test cases to repro missing dependencies found in Airbnb's monorepo, and then fix the underlying issues. In addition to these fixes helping our "jdeps compile-time pruning" optimization, these fixes should also help when the normal Bazel IntelliJ Plugin is used, as it also consults jdeps when deciding what jars to import into IntelliJ.

# Test Plan:
* Wrote Kotlin and Java test cases to repro missing dependencies found in the Airbnb monorepo
* Updated Airbnb's minimal rules_kotlin fork to use the changes in this PR, which has been successfully running CI for over a week.
@@ -46,6 +46,7 @@ class KotlinBuilderJvmJdepsTest(private val enableK2Compiler: Boolean) {
val ctx = KotlinJvmTestBuilder()

val TEST_FIXTURES_DEP = Dep.fromLabel(":JdepsParserTestFixtures")
val TEST_FIXTURES2_DEP = Dep.fromLabel(":JdepsParserTestFixtures2")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

To enable test cases where Java deps depend on other Java deps I added a second set of java test fixtures here

@@ -1385,7 +1621,7 @@ class KotlinBuilderJvmJdepsTest(private val enableK2Compiler: Boolean) {

assertExplicit(jdeps).contains(depWithFunction.singleCompileJar())
assertImplicit(jdeps).contains(depWithReturnType.singleCompileJar())
assertImplicit(jdeps).doesNotContain(depWithReturnTypesSuperType)
assertImplicit(jdeps).contains(depWithReturnTypesSuperType.singleCompileJar())
Copy link
Contributor Author

@scosenza scosenza Feb 26, 2024

Choose a reason for hiding this comment

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

Note that the prior assertion always succeeded as it was missing .singleCompileJar()
When .singleCompileJar() is correctly added, the assertion fails.
We can confirm that depWithReturnTypesSuperType is needed for compilation by commenting out:
c.addTransitiveDependencies(depWithReturnTypesSuperType)
and then seeing the compilation fail with:

error: supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
    class something.SomeType, unresolved supertypes: something.SomeSuperType

Because the super type is actually needed, we've changed the assertion to assert that it is an implicit dep.

@@ -1659,7 +1896,34 @@ class KotlinBuilderJvmJdepsTest(private val enableK2Compiler: Boolean) {

assertExplicit(jdeps).containsAtLeast(depWithFunction.singleCompileJar(), depWithReturnType.singleCompileJar())
assertImplicit(jdeps).doesNotContain(depWithReturnType.singleCompileJar())
assertImplicit(jdeps).doesNotContain(depWithReturnTypesSuperType)
assertImplicit(jdeps).contains(depWithReturnTypesSuperType.singleCompileJar())
Copy link
Contributor Author

@scosenza scosenza Feb 26, 2024

Choose a reason for hiding this comment

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

Note that the prior assertion always succeeded as it was missing .singleCompileJar()
When .singleCompileJar() is correctly added, the assertion fails.
We can confirm that depWithReturnTypesSuperType is needed by commenting out:
c.addTransitiveDependencies(depWithReturnTypesSuperType)
and then seeing the compilation fail with:

error: supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
    class something.SomeType, unresolved supertypes: something.SomeSuperType

Because the super type is actually needed, we've changed the assertion to assert that it is an implicit dep.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I have a PR up that does full Deps diffing which should hopefully avoid issues like this where assertions aren't being correctly done.

https://github.com/bazelbuild/rules_kotlin/pull/1116/files

@scosenza scosenza marked this pull request as ready for review February 26, 2024 18:50
)
}

println("=====================================")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Did you mean to check this in?

Copy link
Contributor Author

@scosenza scosenza Feb 27, 2024

Choose a reason for hiding this comment

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

Thanks for flagging. Removed.

java_library(
name = "JdepsParserTestFixtures2",
testonly = True,
srcs = glob(["testFixtures2/*.java"]),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Might look less copy-paste grouping these different test figures inside directories within the testFixtures directory instead of just duplicating and adding a numerical index. Ex: testFixtures/a-sources/ testFixtures/b-sources/

@Bencodes
Copy link
Collaborator

Bencodes commented Mar 1, 2024

@scosenza are you able to rebase this branch and resolve the conflicts + organize the test fixtures?

@scosenza
Copy link
Contributor Author

scosenza commented Mar 2, 2024

FYI, it looks like WorkerEnvironmentTest may be flakey, as it's passing on my local Mac machine, and passing in the Linux CI build. Update: I just triggered a new CI build by adding a newline, and now WorkerEnvironmentTest is passing.

@scosenza
Copy link
Contributor Author

scosenza commented Mar 2, 2024

@scosenza are you able to rebase this branch and resolve the conflicts + organize the test fixtures?

@Bencodes I just updated my branch with the latest upstream changes. And thanks again for greatly improving the assertions!

import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
import org.jetbrains.kotlin.js.translate.callTranslator.getReturnType
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this the right function to be using? It looks like it's being loaded from the javascript package.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I briefly looked around and didn't see anything that stands out. If anything you can just inline the function itself, which is just calling into another function with resolvedCall as an argument.

Copy link
Contributor Author

@scosenza scosenza Mar 4, 2024

Choose a reason for hiding this comment

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

Thanks for flagging this.

@Bencodes Bencodes merged commit e528366 into bazelbuild:master Mar 4, 2024
2 checks passed
Bencodes pushed a commit to Bencodes/rules_kotlin that referenced this pull request Mar 13, 2024
…azelbuild#1118)

* Improve Kotlin jdeps logic to find more explicit and implicit dependencies (bazelbuild#17)

# Problem
Kotlin jdeps do not collect many direct and indirect dependencies that are required for compilation. These missing jdep found dependencies were discovered as Airbnb tests a "jdeps compile-time pruning" optimization which provides significant IntelliJ indexing, UI, and compilation performance benefits in our fork of the [Bazel IntelliJ Plugin](https://github.com/bazelbuild/intellij).

# Solution
As a followup to bazelbuild#1045, write test cases to repro missing dependencies found in Airbnb's monorepo, and then fix the underlying issues. In addition to these fixes helping our "jdeps compile-time pruning" optimization, these fixes should also help when the normal Bazel IntelliJ Plugin is used, as it also consults jdeps when deciding what jars to import into IntelliJ.

# Test Plan:
* Wrote Kotlin and Java test cases to repro missing dependencies found in the Airbnb monorepo
* Updated Airbnb's minimal rules_kotlin fork to use the changes in this PR, which has been successfully running CI for over a week.

* formatting

* formatting

* revert

* Remove println used for testing

* use new assertion style in jdeps test

* add newline to trigger another build

* Remove class in "js" package, that shouldn't be used and also is no longer needed.

* Remove class in "js" package, that shouldn't be used and also is no longer needed.

---------

Co-authored-by: Steve Cosenza <steve.cosenza@airbnb.com>
@kolloch
Copy link
Contributor

kolloch commented Apr 23, 2024

@scosenza the uses of the jdeps pruning sound really exciting. Any chance that people outside of airbnb could use a similar setup or does it all depend on your bazel intellij plugin fork?

oliviernotteghem pushed a commit to uber-common/rules_kotlin that referenced this pull request Aug 2, 2024
…azelbuild#1118)

* Improve Kotlin jdeps logic to find more explicit and implicit dependencies (bazelbuild#17)

# Problem
Kotlin jdeps do not collect many direct and indirect dependencies that are required for compilation. These missing jdep found dependencies were discovered as Airbnb tests a "jdeps compile-time pruning" optimization which provides significant IntelliJ indexing, UI, and compilation performance benefits in our fork of the [Bazel IntelliJ Plugin](https://github.com/bazelbuild/intellij).

# Solution
As a followup to bazelbuild#1045, write test cases to repro missing dependencies found in Airbnb's monorepo, and then fix the underlying issues. In addition to these fixes helping our "jdeps compile-time pruning" optimization, these fixes should also help when the normal Bazel IntelliJ Plugin is used, as it also consults jdeps when deciding what jars to import into IntelliJ.

# Test Plan:
* Wrote Kotlin and Java test cases to repro missing dependencies found in the Airbnb monorepo
* Updated Airbnb's minimal rules_kotlin fork to use the changes in this PR, which has been successfully running CI for over a week.

* formatting

* formatting

* revert

* Remove println used for testing

* use new assertion style in jdeps test

* add newline to trigger another build

* Remove class in "js" package, that shouldn't be used and also is no longer needed.

* Remove class in "js" package, that shouldn't be used and also is no longer needed.

---------

Co-authored-by: Steve Cosenza <steve.cosenza@airbnb.com>
oliviernotteghem pushed a commit to uber-common/rules_kotlin that referenced this pull request Aug 2, 2024
…azelbuild#1118)

* Improve Kotlin jdeps logic to find more explicit and implicit dependencies (bazelbuild#17)

# Problem
Kotlin jdeps do not collect many direct and indirect dependencies that are required for compilation. These missing jdep found dependencies were discovered as Airbnb tests a "jdeps compile-time pruning" optimization which provides significant IntelliJ indexing, UI, and compilation performance benefits in our fork of the [Bazel IntelliJ Plugin](https://github.com/bazelbuild/intellij).

# Solution
As a followup to bazelbuild#1045, write test cases to repro missing dependencies found in Airbnb's monorepo, and then fix the underlying issues. In addition to these fixes helping our "jdeps compile-time pruning" optimization, these fixes should also help when the normal Bazel IntelliJ Plugin is used, as it also consults jdeps when deciding what jars to import into IntelliJ.

# Test Plan:
* Wrote Kotlin and Java test cases to repro missing dependencies found in the Airbnb monorepo
* Updated Airbnb's minimal rules_kotlin fork to use the changes in this PR, which has been successfully running CI for over a week.

* formatting

* formatting

* revert

* Remove println used for testing

* use new assertion style in jdeps test

* add newline to trigger another build

* Remove class in "js" package, that shouldn't be used and also is no longer needed.

* Remove class in "js" package, that shouldn't be used and also is no longer needed.

---------

Co-authored-by: Steve Cosenza <steve.cosenza@airbnb.com>
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.

3 participants