Overview
The sync process is what enables all the rich features of the IntelliJ IDE, including syntax highlighting, autocomplete, and code navigation. The build system (e.g. Bazel, Gradle) is responsible for transferring its knowledge of the state of the world into models that can be processed by IntelliJ. The sync and indexing process needs to complete before you can take advantage of using IntelliJ, so it’s important to understand what’s happening behind the scenes.
For the Gradle build system, the information about subprojects, build targets, and dependencies are transferred with Gradle’s Tooling API. The Tooling API provides a programmatic way for external applications to get access to information instead of needing to parse information from .gradle
files. Essentially a client can request models that have been previously registered with Gradle (e.g. see this code snippet as an example).
One limitation is that the sync process currently does not take advantage of configuration caching. The sync process is also quite complex and relies a lot on different approaches (e.g. dynamic loading of model builder classes) to create the models. You can begin observing how this process works by reviewing the IDE logs and noticing how the Gradle Tooling API being invoked with an ijinit.gradle
script:
2023-09-11 08:33:35,293 [ 31980] INFO - #o.j.p.g.s.e.GradleExecutionHelper - Passing command-line args to Gradle Tooling API:
--init-script /private/var/folders/1v/v3_87mt519v6gvh3yl0vyr480000gn/T/ijmapper.gradle -Didea.sync.active=true -Didea.resolveSourceSetDependencies=true
-Porg.gradle.kotlin.dsl.provider.cid=23919243869208 --init-script /private/var/folders/1v/v3_87mt519v6gvh3yl0vyr480000gn/T/sync.studio.tooling.gradle
-Djava.awt.headless=true --continue --stacktrace -Pandroid.injected.build.model.only=true -Pandroid.injected.build.model.only.advanced=true
-Pandroid.injected.invoked.from.ide=true -Pandroid.injected.build.model.only.versioned=3
-Pandroid.injected.build.model.disable.src.download=true -Pidea.gradle.do.not.build.tasks=true
-Dorg.gradle.internal.GradleProjectBuilderOptions=omit_all_tasks -Pkotlin.mpp.enableIntransitiveMetadataConfiguration=true
--init-script /private/var/folders/1v/v3_87mt519v6gvh3yl0vyr480000gn/T/ijinit.gradle
This script loads the ExtraModelBuilder, which in turn tries to find classes that extend ExtraModelBuilder
or ModelBuilderService
. Attaching a breakpoint to the Gradle daemon shows the different model builders involved, some of which are needed for Kotlin language support.
These model builders are only invoked if they’re requested by the client. Notice in this code how the canBuild()
method is invoked, which primarily checks to see if the model name requested matches that of what the model builder provides.
for (ModelBuilderService service : modelBuilderServices) {
if (service.canBuild(modelName)) return true;
}
Because of the extensibility of the IntelliJ platform, there are other ways in which models are created and augmented. You’ll need to attach to both the Gradle daemon and the Android Studio process to study this mechanism in closer detail! Read this blog post for a historical context of the sync process too!