The JetBrains Academy plugin supports several versions of the IntelliJ platform as well as different IDEs (IntelliJ IDEA, CLion, PyCharm, etc.). This document describes how multi-platform support is organized in the project, how to write platform-specific code, and how to support new platform versions or drop old ones.
We usually support the two latest releases of the platform (for example, 2025.3 and 2026.1) or the latest release and the current EAP.
To be sure the plugin works with all releases, we have to build the plugin with different versions of IDE.
The plugin project has separate settings for each platform version (in general, IDE and plugin versions)
to avoid manual changes when you want to compile the project with a non-default platform version.
These settings are stored in gradle-%platform.version%.properties files.
Sometimes there are not compatible changes in new platform version. To avoid creating several parallel vcs branches for each version, we have separate directories for each version to keep platform-dependent code.
For example, com.jetbrains.edu.cpp.CppCmakeCompatibilityUtilsKt.getBundledCMakeToolBinary function in Edu-Cpp module
should have separate implementations for 253 and 261 platforms.
Then the source code structure of the corresponding module will be
+-- Edu-Cpp
| +-- branches/253/src
| +-- com/jetbrains/edu/cpp
| +-- cppCmakeCompatibilityUtils.kt
| +-- branches/261/src
| +-- com/jetbrains/edu/cpp
| +-- cppCmakeCompatibilityUtils.kt
| +-- src
| +-- other platform independent code
Of course, only one batch of platform-specific code will be used in compilation.
To change the platform version which you use during compilation,
i.e. change IDE abd plugin dependencies, and platform-dependent code,
you need to modify environmentName property in gradle.properties file or
pass -PenvironmentName=%platform.version% argument to gradle command.
See Tips and tricks section to get more details on how to create platform-specific code.
The plugin is available in several IDEs like IDEA, PyCharm, CLion, WebStorm, etc.
Generally, they provide common core API that can be safely used in the plugin. But they also provide some specific API
available only in particular IDE (for example, java-api and java-impl modules in IDEA).
To prevent accidental usage of specific API in common code, we have to build plugin/run with different IDEs.
At the moment, the plugin tests can be launched with IDEA, CLion, and PyCharm.
You can change this IDE via baseIDE property in gradle.properties file.
Note, not all modules can be compiled with any IDE. For example, Edu-Cpp module is supposed to be built
only with CLion.
Such restrictions are already taken into account in the configuration of the corresponding modules
in the corresponding build.gradle.kts and when you choose base IDE for build unsupported in the particular module,
it will be compiled with some default IDE.
Please, always remember the general advice - if you have to extract code into platform-specific source sets, try to minimize it. It will simplify maintenance of this code in the future.
A non-exhaustive list of tips on how you can adapt your code for several platforms:
- to execute specific code for a certain platform in gradle build scripts (
build.gradle.ktsorsettings.gradle.kts), just useenvironmentNameproperty andif/whenconditions - if you need to have different sources for each platform:
- check that you actually need to have specific code for each platform.
There is quite often a deprecated way to make necessary action.
If it's your case, don't forget to suppress the deprecation warning and add
// BACKCOMPAT: %platform.version%comment to mark this code and fix the deprecation in the future.// BACKCOMPAT: %platform.version%means that the corresponding code should be fixed when we will stop support for%platform.version% - extract different code into a function and place it into
compatibilityUtils.ktfile in each platform-specific source set. It usually works well when you need to call specific public code to make the same things in each platform - if code that you need to call is not public (for example, it uses some protected methods of parent class), use the inheritance mechanism.
Extract
AwesomeClassBasefrom yourAwesomeClass, inheritAwesomeClassfromAwesomeClassBase, moveAwesomeClassBaseinto platform-specific source sets, and move all platform-specific code intoAwesomeClassBaseas protected methods. - sometimes, signatures of some methods might have changed during platform evolution.
For example,
protected abstract void foo(Bar<Baz> bar)can be converted intoprotected abstract void foo(Bar<? extends Baz> bar)since%platform.version%and you have to override this method in your implementation. It introduces source incompatibility (although it doesn't break binary compatibility). The simplest way to make it compile for each platform is to introduce platform-specifictypealias, i.e.typealias PlaformBar = Bar<Baz>for platforms before%platform.version%andtypealias PlaformBar = Bar<out Baz>for%platform.version%and newer, and use it in signature of overridden method. Also, this approach works well when some class you depend on was moved into another package. - if the creation of a platform-specific source set is too heavy for your case, there is a way how you can choose specific code in runtime.
Just create the corresponding condition using
com.intellij.openapi.application.ApplicationInfo.getBuild. Usually, it looks like
Of course, code should be compilable with all supported platforms to use this approach. This approach can be used to temporarily disable some tests to find out why they don't work later.val BUILD_%platform.version% = BuildNumber.fromString("%platform.version%")!! if (ApplicationInfo.getInstance().build < BUILD_%platform.version%) { // code for current platform(s) older than %platform.version% } else { // code for %platform.version% platform and newer }
- check that you actually need to have specific code for each platform.
There is quite often a deprecated way to make necessary action.
If it's your case, don't forget to suppress the deprecation warning and add
- if you need to register different items in
%xml_name%.xmlfor each platform:- create
platform-%xml_name%.xmlin all%module_name%/branch/%platform.version%/resources/META-INFdirectories - put platform-specific definitions into these xml files
- include platform-specific xml into
%module_name%/resources/META-INF/%xml_name%.xml, i.e. add the following code
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude"> <xi:include href="/META-INF/platform-%xml_name%.xml" xpointer="xpointer(/idea-plugin/*)"/> </idea-plugin>
- create
- Develop feature in feature branch
%feature.branch% - Commit changes
- Change
environmentNamevalue ingradle.propertieswith%platform.name%where%platform.name%is the desired platform. - Run tests to ensure that everything works
Everything works:
- Push all the changes
Something went wrong (some API changes):
- Ensure that you can't use the old API instead of the new one for all platforms. If you can, just modify your current implementation.
Note, if you use deprecated API, don't forget to suppress deprecation warning and
add
// BACKCOMPATcomment (see more in Tips and tricks section) - If there isn't a common API for all platforms, extract problem pieces of code into separate files as new functions or classes
- Create implementations for each platform and place them into
branches/%platform.name%/srcfolders of the corresponding module - Commit new files
- Push all the changes
When the new IntelliJ version comes out, we need to prepare plugin's build with the new version and handle all the incompatible changes in API. To reduce the number of supported IntelliJ versions, we also drop versions support from time to time.
The following instructions use old, current, and new terms:
old- the oldest supported platform version that should be droppedcurrent- number (or numbers, depends on context) of the latest major stable platform release(s) that are supported by the pluginnew- new platform version that should be supported
For example, at the moment of writing, 193 is old, 201 and 202 are current, and 203 is new.
See build_number_ranges for more info about platform versions.
Step-by-step instruction:
-
Add support for the
newplatform:- add
gradle-%new%.propertieswith all necessary properties - copy all
%module_name%/branches/%current%directories for the latest supported platform as%module_name%/branches/%new% - make it compile
The last step may require extracting some code into platform-specific source sets to make the plugin compile with each supported platform. See Tips and tricks section for the most common examples of how to do it.
- add
-
Fix tests. If you don't know how to fix some tests (especially if you are not a maintainer of the corresponding subsystem), just create an issue in YouTrack.
-
Update TeamCity configuration to provide a new subproject instead of the old one. At the moment of writing, it requires changing
EducationalSupportedReleasesinintellij-teamcity-configrepo.
- Drop all code related to the
oldplatform version:- remove
gradle-%old%.properties - remove all
%module_name%/branches/%old%directories in each module
- remove
- Move common code from all current platform-specific
%module_name%/branches/%current%directories into the common source set, i.e. from%module_name%/branches/%current%into%module_name%. Also, simplify the moved code if possible.Compare Withaction may help you to determine identical files in platform-specific directories. - Fix
// BACKCOMPAT: %old%comments. You can find all such comments viaFind in Pathaction. Also, you can add\bbackcompat\b.*pattern intoPreferences | Editor | TODOsetting panel to show such comments inTODOtool window.