Debugging a databinding compiler error
During a refactor of our app, we moved some classes and layout files into different flavor configurations. At the end of the refactor, after running a build, we received a build error with the following message:
Cannot resolve type for viewModel
at android.databinding.tool.util.L.printMessage(L.java:134)
at android.databinding.tool.util.L.e(L.java:107)
at android.databinding.tool.expr.Expr.getResolvedType(Expr.java:371)
The error message is quite good and understandable. So the type is wrong for the viewModel variable. Our only problem was, that we use the variable name viewModel in all of our activity and fragment layout files, and there’s no filename or type in the error message. So we could review the files one by one, and check that the type exists (in all flavors or in the main flavor), but that would take a lot of time, and is prone to errors.
But we do have a stack trace, so wouldn’t it be nice if we could attach a debugger to the databinding compiler class just when it discovers this error?
Setting up Android Studio to stop at breakpoints
First, let’s prepare our IDE, which is Android Studio in this case, but this will tutorial probably applies to IntelliJ IDEA as well.
First, let’s add a new run configuration. Go to Edit → Run configurations…, and in the top left corner click on the plus button to add a new configuration. Select Remote, and don’t change anything else (maybe rename it from the default Unnamed). In the middle there’s a section named “Command line arguments for remote JVM”, copy the text from the textbox, it should look something like this:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
Click on OK, your current run configuration will change to this new one.
Note how the green play button to Run a build is now disabled, and you only have debug now. This is fine, since we will debug a build.
Now place the breakpoints as you usually would in an Android build. You can look up the compiler classes using the Find window or with Search everywhere (press Shift twice). Just make sure you allow Studio to search in non-project files.
Running the build from the command line
Now, open a terminal, and go to the project root. Start typing the command you would run to create a build, such as `./gradlew assembleGreenskinAcceptanceDebug`
But then also append the following parameters:
--no-daemon -Dorg.gradle.debug=true -Dkotlin.compiler.execution.strategy="in-process" -Dkotlin.daemon.jvm.options="-agentlib:jdwp=transport=dt_socket\,server=y\,suspend=n\,address=5005"
The final line in the quotes should be the line you copied from Android Studio, correctly escaped for your terminal you are using.
So altogether you should have a command like this:
./gradlew assembleGreenskinAcceptanceDebug --no-daemon -Dorg.gradle.debug=true -Dkotlin.compiler.execution.strategy="in-process" -Dkotlin.daemon.jvm.options="-agentlib:jdwp=transport=dt_socket\,server=y\,suspend=n\,address=5005"
Connecting the debugger to the build process
Run the command now in the terminal and it will start building your app. After starting the process, immediately switch over to Android Studio, and click on the green debug icon. You should see the blue message Connected to the target VM, address: ‘localhost:5005’, transport: ‘socket’ if the connection to the remote process was successful.
The build process should now stop on your breakpoints, assuming that the file lines match up (for me they didn’t, but after some playing around with the position of the breakpoint, I had a hit).
When the breakpoint is hit, you can inspect all the variables. From here I could find the type of my variable easily, which pinpointed the issue to the file which was indeed missing.
To sum it all up, it is a great thing that I am able to have such insight into the compiler, but it would have saved quite some time if the error message would have been more verbose. So I created this ticket to make it easier for people who would get this error in the future.