[Native Image] Error > 'other' has different root when building
Describe the Issue I’m not sure why this happens, but I installed GraalVM JDK on the C drive and set both JAVA_HOME and GRAALVM_HOME to the full installation path. I also verified that the native-image tool is available on all drives. However, the issue is that I can only build native images on the C drive. When I try to build on a different drive (like D or E), I get the following error: *** What went wrong: Execution failed for task ':nativeCompile'. 'other' has different root**
plugins {
id 'java'
id 'org.graalvm.buildtools.native' version '0.11.0'
}
group = 'org.test'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
graalvmNative {
binaries {
main {
imageName = 'application'
mainClass = 'org.test.Main'
debug = true
verbose = true
fallback = true
sharedLibrary = false
quickBuild = false
richOutput = false
}
}
}
Stacktrace is this:
Caused by: java.lang.IllegalArgumentException: 'other' has different root
at org.graalvm.buildtools.utils.NativeImageUtils.convertToArgsFile(NativeImageUtils.java:104)
at org.graalvm.buildtools.gradle.internal.NativeImageCommandLineProvider.asArguments(NativeImageCommandLineProvider.java:238)
at org.graalvm.buildtools.gradle.tasks.BuildNativeImageTask.buildActualCommandLineArgs(BuildNativeImageTask.java:277)
at org.graalvm.buildtools.gradle.tasks.BuildNativeImageTask.exec(BuildNativeImageTask.java:306)
You can temporarily set something like this in you global gradle.properties or via the environment:
org.gradle.jvmargs=-Djava.io.tmpdir=D:/Temp
It happens when your project and temp folder are on different drives.
Same here, I'm on windows, after moving my project to C drive the issue is gone. But it is not a good solution at all.
As I search on web, found that android studio used to have this error when generating apk files if a "destination path" setting is not set properly. I wonder if graalvm's gradle plugin can provide similar setting for nativeCompile task.
For me it is happening on GitHub Action runners since I don't have a windows box or VM at home.
Also, my versions: Gradle 9.1, build tools 0.11.2
exception
* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':jufmt-cli:nativeCompile'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:135)
at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:288)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:133)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:121)
at org.gradle.api.internal.tasks.execution.ProblemsTaskPathTrackingTaskExecuter.execute(ProblemsTaskPathTrackingTaskExecuter.java:41)
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
Caused by: java.lang.IllegalArgumentException: 'other' has different root
at org.graalvm.buildtools.utils.NativeImageUtils.convertToArgsFile(NativeImageUtils.java:104)
at org.graalvm.buildtools.gradle.internal.NativeImageCommandLineProvider.asArguments(NativeImageCommandLineProvider.java:238)
at org.graalvm.buildtools.gradle.tasks.BuildNativeImageTask.buildActualCommandLineArgs(BuildNativeImageTask.java:277)
at org.graalvm.buildtools.gradle.tasks.BuildNativeImageTask.exec(BuildNativeImageTask.java:306)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:125)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:252)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:237)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:220)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:203)
at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:170)
at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:105)
at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:44)
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:59)
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:56)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:56)
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:44)
at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:42)
at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:75)
at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:50)
at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:28)
Thanks to the original report, I looked at the post setup step and noticed that multiple drives are at play:
The daemon is running on D and my repo was checked out on D as well:
Stopping Gradle daemons for D:\a\.gradle\wrapper\dists\gradle-9.1.0-bin\9agqghryom9wkf8r80qlhnts3\gradle-9.1.0
C:\Windows\system32\cmd.exe /D /S /C "D:\a\.gradle\wrapper\dists\gradle-9.1.0-bin\9agqghryom9wkf8r80qlhnts3\gradle-9.1.0\bin\gradle.bat --stop"
Initializing the repository
"C:\Program Files\Git\bin\git.exe" init D:\a\jufmt\jufmt
The toolchain is however on C:
env:
GRAALVM_HOME: C:\hostedtoolcache\windows\graalvm-community-jdk-25.0.1_windows-x64_bin\25.0.1\x64\graalvm-community-openjdk-25.0.1+8.1
JAVA_HOME: C:\hostedtoolcache\windows\graalvm-community-jdk-25.0.1_windows-x64_bin\25.0.1\x64\graalvm-community-openjdk-25.0.1+8.1
NATIVE_IMAGE_OPTIONS: -H:BuildOutputJSONFile=C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\native-image-build-output.json
The task a hand:
named<BuildNativeImageTask>("nativeCompile") {
classpathJar.fileProvider(shadowJar.map { it.archiveFile.get().asFile })
}
So, the error looks like the plugin tries to create an args file in a temp location, but it cannot relativize paths from different root (here C and D).
Thanks to @megla-tlanghorst's comment, I applied the same fix, but only in my GHA workflow:
- name: Build native image
run: ./gradlew nativeCompile --stacktrace
+ env:
+ # Needed for Windows; to resolve issue with temp folder during native image build, due to relativization
+ # problem when different drives are used, such as in GHA runners.
+ # https://github.com/graalvm/native-build-tools/issues/754
+ GRADLE_OPTS: -Djava.io.tmpdir=${{ runner.temp }}
On Windows GHA runners, the runner.temp will use the temp location of the "attached" drive, e.g. D:\a\_temp.
More doc : https://docs.github.com/en/actions/reference/workflows-and-actions/contexts
@melix mentioned in https://github.com/oracle/graal/issues/11795#issuecomment-3415849736
This is caused by https://github.com/graalvm/native-build-tools/blob/a5cd92d30aab3bdb50033b85f5f7a08c955cf871/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java#L236-L239 so this issue should probably be moved to NBT.