Tips & Tricks: Develop OpenJDK in CLion with Pleasure

You’ve probably heard that dogfooding at JetBrains is a mission-critical part of our development process. You may also know that we build our own JetBrains Runtime to provide a runtime environment for running IntelliJ-based products. It’s based on OpenJDK, and so, obviously, it’s a great opportunity for dogfooding in CLion.

Besides, there is a huge community contributing to OpenJDK that often asks about tips & tricks for dealing with the native part of the OpenJDK project in CLion. There are already several tried and tested collections of tips & tricks from the community leads. In this blog post, we’d like to share our own tips & tricks from our own experience of developing OpenJDK in CLion.

Step 1: Get the source code and the tooling ready

You can get the latest OpenJDK sources either from the official mercurial repo (hg clone https://hg.openjdk.java.net/jdk/jdk) or from the GitHub mirror (might be faster). Please carefully read how to prepare the local environment.

Download either the latest version of CLion from our site or check out the recent CLion 2020.1 EAP build (this one offers a better debugging experience on Windows).

Step 2: Open project in CLion

While there is ongoing work to natively support Makefile projects in CLion, for this instruction we’ll be using an OpenJDK compilation database to work with the project in CLion.

  1. Read how to create the compilation database for the OpenJDK native code ${source_root}/doc/ide.md.
  2. Select the build target. If you want to debug your build, the best option is to do configure --with-debug-level=fastdebug.
  3. Run make compile-commands to generate the project for all the source code. You can also run make compile-commands-hotspot to generate the project for HotSpot only.
  4. Make sure you now have a compile_commands.json file in the ${source_root}/build/<target>/ directory.
  5. For some modules, precompiled headers are used. Because of this we suggest building OpenJDK before generating the project model. To build binaries, just call make.
  6. Refer to CLion’s online help on compilation databases to understand what they are and the possible ways you can generate one.
  7. On Windows, there are a few issues with the OpenJDK compilation database (some are CLion issues and we plan to address them in the future, some are just because of an incorrect compilation database on the OpenJDK side, but we can try and workaround them in CLion): CPP-19223, CPP-19225, CPP-19224. We have this short java program to fix the issues. There is no need to compile it; just do java <path/to/FixCompileCommands.java> <absolute/path/to/generated/compile_commands.json>. The original file will be saved as compile_commands.json.old.
  8. Make sure that you have configured toolchains in CLion. On Windows, configure and set Visual Studio as the default (make it the first in the list):
    win_toolchains
  9. Now open the generated compile_commands.json as a project in CLion. It will take approximately 20 seconds to load on an average machine.
  10. By default, the project root is set to the directory containing the compilation database file. So change the project root directory to ${source_root}.

Now the project is ready in CLion!

Step 3: Generate Run Configurations and Build Targets

The compilation database itself lacks the data required for building, running, and debugging an application. However, you can set up the workflow by adding custom build targets for your compilation database project and then creating custom Run/Debug configurations for these targets.

Custom build targets for OpenJDK

To configure custom build targets, go to Settings/Preferences | Build, Execution, Deployment | Custom Build Targets:

  1. Use the default toolchain on Linux and macOS. On Windows, use the Visual Studio toolchain.
  2. Configure the new external tools for the build and clean steps:
    custom_target
  3. On Linux and macOS: set a name and description, select make as the program, and specify the make arguments that should be used to build your image. In our case, the only argument is the make configuration: CONF=macosx-x86_64-server-fastdebug. Set ${source_root} as the Working Directory:
    1. fastdebug
    2. fastdebug_clean
  4. On Windows, Unix build tools are still an essential part of the build process, so we run build and clean via Cygwin (or you can do it via WSL). To do this, set the program field to something like C:\cygwin64\bin\bash and arguments as --login -c "cd /cygdrive/c/${source_root}; make CONF=windows-x86_64-server-fastdebug" for build and --login -c "cd /cygdrive/c/${source_root}; make clean CONF=windows-x86_64-server-fastdebug" for clean:
    edit_tool2

Custom Run/Debug configurations

Go to Run | Edit Configurations and start a new Run and Debug configuration based on the Custom Build Application template.

  1. Select the custom target you created earlier as Target.
  2. Set Executable to the java binary built as part of an image (in our case it is ${source_root}/build/macosx-x86_64-server-fastdebug/jdk/bin/java)
  3. Set Program Arguments.
  4. The JDK build system is smart, and incremental compilation is pretty fast. However, if you don’t want to rebuild OpenJDK before each run or debug, you can disable build step in the Before launch section of the run configuration settings:
    jdk_build3

We are almost there!

Step 4: A few debugging tips

We debug with the LLDB debugger. This includes Windows, where CLion comes with the bundled LLDB-based debugger for the Microsoft Visual C++ toolchain.

  1. On Linux and macOS, create a custom .lldbinit file in the same directory where the compile_commands.json file was generated.

    • Add pro hand -p true -s false SIGSEGV SIGBUS to this file to suppress signal handling.
    • Don’t forget to explicitly allow LLDB to use the config file from the project directory.
  2. Some debugger features are not available (that is, they do not automatically show all local variables for the frame) for the binary built with fastdebug configuration. You still can watch any specific variable manually. If you need to debug the binary without any optimizations, switch to the slowdebug configuration.
  3. Set a breakpoint (for example, on a line of the Arguments::init_system_properties function) and click the debug button.
  4. CLion should stop on a breakpoint in your code:
    image2

That’s it! Do you have any questions or feedback? Try it out and tell us what you think in the comments below.

Your CLion team
JetBrains
The Drive to Develop

This entry was posted in Tips'n'Tricks and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *