Executable built on Cygwin, runs on Win32, tries and fail to use '/tmp'

Hello,

Is it normal if an executable built on Cygwin and running “on a plain Windows box with no Cygwin, just four Cygwin DLLs” still expect the Unix-like file hierarchy with /tmp? This is apparently what’s happening for a contributor who’s trying to build our program on Windows.

It has to do with the Filename module using Sys.os_type string to determine the temporary folder. We’re guessing that Sys.os_type is frozen at compile time to "Cygwin" rather than reflecting the runtime environment, which may have no /tmp path. If that’s the case:

  • Is this a bug?
  • Is there a good workaround or a more proper way to achieve this?

We’re aware of:

  • making a fully native Windows build - we can’t for now due a reliance on Unix.fork()
  • setting the TMPDIR environment variable to . or some other value acceptable on a Windows filesystem.

I’m the user @mjambon mentioned :blush: and here’s what we found after some digging—I don’t think there’s a bug anywhere, I just had to learn more about how Cygwin works. Here’s what we now know—

If you build an OCaml binary in Cygwin and then copy the binary (plus cygwin1.dll and other DLLs required by the executable) to a fresh Windows install that doesn’t have Cygwin on it, you can, of course, run the executable.

If your code used Filename.temp_file, the executable will likely try to access /tmp, because, as @mjambon noted, when OCaml runs in Cygwin, it uses Unix conventions for the temp directory, which are:

  let temp_dir_name =
    try Sys.getenv "TMPDIR" with Not_found -> "/tmp"

We hypothesize that the environment at compile time gets frozen in the executable.

So if you put cygwin1.dll in c:\ocaml-stuff, then /tmp corresponds to c:\ocaml-stuff\..\tmp, i.e., c:\tmp. An easy way to understand this is: in Cygwin, cygwin1.dll expects to live in /bin, so /tmp corresponds to a sibling subdirectory. So one option is to create this tmp directory: your OCaml executable will happily use that.

However, as the code snippet above indicates, you can override this by explicitly setting TMPDIR environment variable. If I run set TMPDIR=. before running my OCaml executable, it will use the current directory as the temp directory, not /tmp.

(A nice bonus is, because Cygwin understands both Windows paths like C:\Users\User and Unix paths like /cygdrive/c/User/Users, you can use either with your OCaml binary.)

It might be nice if an OCaml executable could detect at runtime whether it was running in Windows or Cygwin and adjust Sysdeps accordingly, but I do love the elegance of the current approach.

1 Like

I’ve always been wondering if the Sys.os_type is a compile time or a run time thing. Where is it defined in the stdlib source?

I think there’s a bit of confusion here. You either build an OCaml program for native Windows (using either MSVC or mingw-w64) or for Cygwin (build with Cygwin GCC, linked with cygwin1.dll). Cygwin provides posix emulation for programs that were build with Cygwin’s GCC and linked with cygwin1.dll.
Cygwin also provides standard unix tools such as make, sed, bash, but you can use a native Windows program in a Cygwin bash shell without having it linked to cygwin1.dll. It’s very convenient to use the Cygwin environment to build OCaml programs (and the OCaml compiler), but do not confuse build-in-cygwin with build-in-cygwin-gcc-and-linked-with-cygwin1.dll.

Thus, this doesn’t make a big difference from the point of view of your program:

running it on a plain Windows box with no Cygwin, just four Cygwin DLL

So four Cygwin DLLs isn’t no Cygwin, it’s completely Cygwin but a bit stripped down.

OCaml executable could detect at runtime whether it was running in Windows or Cygwin and adjust Sysdeps accordingly

As above, this doesn’t make sense, although it might be possible to detect if a program parent process is linked with cygwin1.dll or not.

I think that setting the TMPDIR variable is the right call.

I’ve always been wondering if the Sys.os_type is a compile time or a run time thing. Where is it defined in the stdlib source?

At compile-time, grep for OCAML_OS_TYPE in configure.ac and runtime/sys.c.

3 Likes

A Cygwin installation is essentially the directory above cygwin1.dll, so, yes, it will expect to look for tmp in …\tmp. Nothing should be baked into the executable

I wouldn’t recommend attempting to use Cygwin binaries this way - cygwin1.dll isn’t just a dll to be copied (as you can see!).

Is building with mingw-w64 not an option?

3 Likes

Ahh, got it, this is a super-helpful way to frame it, thank you Rucikir!

In that case, maybe I can gently propose a minor improvement to Cygwin’s temp_dir_name: rather than reusing Unix.temp_dir_name wholesale, maybe it can additionally check whether /tmp exists and if not, use . like Win32.temp_dir_name does? That would make it foolproof for these “stripped down” Cygwins where there’s just a few DLLs in a directory and none of the other directory structures in place—a very niche use case I’m sure!

Right. As @mjambon mentioned, the only reason I’m using Cygwin instead of MSVC/OCaml for Windows is—the program currently needs fork and I’d like to run it on Windows today. It certainly is a bit laborious to set up everything just right to get this to work—place the Cygwin DLLs alongside the executable or in the PATH, etc., but it seems to be working well!

2 Likes

Oh right, sorry - I missed the fork comment at the start!

I can gently reply no, that won’t be happening! The program can do this itself easily enough on start-up with Filename.get_temp_dir_name and Filename.set_temp_dir_name.

As noted above, copying the DLLs isn’t creating some stripped-down Cygwin, in quotes or otherwise, it’s full-blown installing Cygwin, merely without base packages. cygwin1.dll isn’t a little layer, it’s a kernel - the first Cygwin executable to start a given DLL literally boots it (if you create …\etc\fstab, you can add mounts to the executable…). Cygwin is a funny flavour of BSD, not (native) Windows - running Cygwin executables from outside a Cygwin shell is an extremely complicated (and error-prone) bonus.

4 Likes