oracle / graal

GraalVM compiles Java applications into native executables that start instantly, scale fast, and use fewer compute resources 🚀
https://www.graalvm.org
Other
20.34k stars 1.63k forks source link

Native-image should provide a cross-platform way to get the path of executable file #4216

Closed nartamonov closed 2 years ago

nartamonov commented 2 years ago

Feature request

Is your feature request related to a problem? Please describe. Native-image could be used to replace scripts (for example, .sh or .cmd) for various system tasks: launchers, backups, utilities, etc. Often such scripts need a way to find out their own directory in order to calculate path to some adjacent directories. For example if we write a launcher that pefrorms some preparatory tasks and then starts another program, in order to start that program launcher might need to calculate the path to the target program relative to himself.

Describe the solution you'd like. Native-image should implement some way to provide the path of running executable module. Ideally that way should be cross-platform.

For example, on Win32 it can use GetModuleFileName while on Linux it can use something like readlink /proc/self/exe.

Describe who do you think will benefit the most. The feature appears to be the most useful for the users of GraalVM which use it to implement system scripts and utilities: developers, system administrators, maybe devops.

Describe alternatives you've considered. I can't find an alternative to the feature. Of course we can use JNI to call Win32 API but it's too much work for simple purposes like writing a script.

rubyFeedback commented 2 years ago

This reminds me a bit of a problem I had with ocra ( https://github.com/larsch/ocra ) in ruby. I could bundle things on windows, but I also was using local yaml files (.yml) which then could not be found since they were not part of the ocra bundle. Made me re-consider depending on .yml files and provide sane defaults in the event no .yml file could be found.

So while my above description is not fitting 1:1 to the threadstarter's rationale given ("might need to calculate the path to the target program relative to himself"), I very much agree with the underlying perception of an issue in regards to paths that may be different or unable to be queried.

kkriske commented 2 years ago

I've done it like this before:

import java.net.URISyntaxException;
import java.nio.file.Path;

public class ExePath {
    public static void main(String[] args) throws URISyntaxException {
        Path path = Path.of(
                ExePath.class.getProtectionDomain()
                        .getCodeSource()
                        .getLocation()
                        .toURI()
        ).getParent();
        System.out.println(path);
    }
}

I know it works on windows, don't know about other platforms. But I don't see why it wouldn't work the same way.

This gives you the folder in which the exe is located, no matter where you call it from. It only works in native-image, as the getLocation call returns null when running in jvm.

nartamonov commented 2 years ago

@kkriske thank you for the advice! I tested that approach on Windows 10 and Linux with GraalVM 21.3.0 and JVM11. Works fine in both cases! I suppose it will work on MacOS too.

I think we can close the issue but it would be helpful for community if the trick will be documented in native-image user manual. I can try to propose a PR if the devs tell where the sources of docs are.

kkriske commented 2 years ago

@nartamonov I did a bit more searching and found this one as part of the native-image SDK: https://github.com/oracle/graal/blob/dff76ed3958669b80c6d20c881fcd5eb448c633c/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ProcessProperties.java#L59

The ProtectionDomain workaround ends up delegating to that same call in native-image, so you should probably just use it directly.

Edit: The implementation does actually call GetModuleFileName for windows, uses /proc/self/exe on linux and calls NSGetExecutablePath on MacOS.

nartamonov commented 2 years ago

@kkriske thanks again, I tried and it works fine! 😃 Turns out there is a whole NativeImage-specific API in the package org.graalvm.nativeimage. Didn't know it. So I suppose it completely settles the issue.