Problem description: oftentimes when you have a binary file, you want to distribute it along with some other resource files (think images and whatnot). You expect that some collection of files containing these images will be distributed together, but you want to be able to move them around on your own system or to a friend's system as one atomic block without breaking everything. This means, effectively, that you have to be able to have the program start with any current directory and understand how to change directories to the directory where "it lives". The easiest way of doing this in Standard ML is to change directories (OS.FileSys.chdir) to whichever directory the commandline argument that ran the program appeared to live in (OS.Path.dir (CommandLine.name ())).
So far so goodWe can illustrate how this works and fails to work with a one-line test program that we compile in MLton.
structure Foo = struct val () = print ("I'm " ^ CommandLine.name () ^ "\n") endLet's save this in /tmp, compile it, and run it:
$ cd /tmpOkay, now wherever we go, the directory part of (CommandLine.name ()) is going to be proper for getting from the current directory of the user into the directory where the binary lives:
$ mlton foo.sml
$ cd ..
$ cd /tmp/baz
Symlinks and paths and lying, lying command linesThe problem's gonna come when we want to execute our file from the path or from a symlink: then, the apparent working directory given by (CommandLine.name ()) will *not* necessarily be the place where the executable itself lives.
$ pwdThe solution adopted by the group of us that wrote the C0 compiler is to notice that if you call something from a shell script it gets its path expanded. Therefore, if we have a shell script doing nothing but calling our program, the program will get called with its absolute path and therefore the program's attempts to chdir into the directory where its binary (and associated resources) live will succeed.
$ export PATH=$PATH:/tmp
$ cd /tmp
$ mkdir baz
$ cd baz
$ ln -s ../foo foo2
$ pwdHowever, this solution only works for things on the $PATH, not for symlinks:
$ mv ../foo ../foo.exe
$ echo '$0.exe $*' > ../foo
$ chmod a+x ../foo
./foo2:1: ./foo2.exe: No such file or directory
Wrapping upCan anyone think of a better way to do this? The shell script is simple, but it seems unnecessary. Perhaps I really should be asking this on StackOverflow, but the Standard ML conversations on StackOverflow are depressing to say the least.
P.S. - If you're distributing your program by way of a SML/NJ heap image, (CommandLine.name ()) will always be sml. To defeat this, when you issue the command that reloads the heap image, add the argument @SMLcmdname=$0. That is, of course assuming you're reloading the heap image from within a shell script, which is the common way to make an executable using SML/NJ. So, in effect, with SML/NJ this problem is "automatically solved" because you're already redirecting through a shell script.
P.P.S. - After I wrote this post, I realized it was probably a language-independent concern. Sure enough, there's StackOverflow posts about this problem from the perspective of C and C++. Seems like it's just a hard problem; crud.