A zoo of values for system

The new pyml uses ocaml-config:system, which returns a zoo of values, instead of the expected linux.

Is this a result of passing incorrect values to configure, or is the usage of ocamlc -config|grep system the wrong way to distinguish between Windows|OSX|Unix?

I would say it is the wrong way, the right way would be to check the C compiler with dune-configurator, that is also portable and can cross compile.

The correct answer likely lies more in what you need to distinguish them for.

If it’s for file extensions this old message by @dra27 has some answers (here’s some code based on it)

At present, this is a documentation error in Dune (see ocaml/dune#4895). The system setting output by ocamlopt -config (which is where Dune takes the value of %{ocaml-config:system}) is not about the operating system at all, and never has been (for example, on s390x, it doesn’t even mention linux). In the compiler it’s used to differentiate calling conventions and so forth.

It’s not clear where this will be fixed - OCaml doesn’t have much interest in the precise operating system, so a more precise value doesn’t necessarily live in the compiler. OCaml’s system setting is also static, where the OS you’re running on is a runtime property. That’s very strongly related to @dbuenzli’s point that it depends on what you’re then doing with the information - do you care that you’re looking at a Windows executable running in Wine on Linux; a static ELF executable compiled for a different distro; that a Windows executable was invoked from within WSL, for examples?

Depending on use-case, opam does have precise, normalised, runtime values for the OS in its os, os-distribution and os-family variables. The logic is in src/state/opamSysPoll.ml but boils down to uname -s with a macOS shim when Sys.os_type = "Unix".

As @EduardoRFS suggests, it should be straightforward in dune-configurator to wrap either uname or uname() in the meantime.

Thanks for all the input. I will leave it to the author to use something else than ocaml-config:system in future releases of pyml.

Thank you very much for all your anwers, and thanks @olafhering for asking this question I should have asked first but didn’t dare to!

pyml needs to distinguish between Linux, Mac OS X and Windows: I copy here the interface pyml_arch.mli (thanks to the present discussion, I added some comments):

(** List of possible names for libpython given major and minor versions.
    On Linux, "libpython%d.%d.so"; on Mac OS X, "libpython%d.%d.dylib";
    on Windows, "python%d%d.dll". There may be some variants to try,
    like "libpython%d.%dm.so", so we return a list. *)
val library_patterns: (int -> int -> string, unit, string) format list

(** Suffix to add to library names returned by pkg-config (".so" on Linux,
    ".dylib" on Mac OSX, we don't use pkg-config on Windows) *)
val library_suffix: string

(** Ensure that the executable has the ".exe" suffix on Windows, do nothing
    on other platforms. *)
val ensure_executable_suffix: string -> string

(** The tool to search in PATH: "which" on Unix/Mac OS X; "where" on Windows. *)
val which: string

(** Convert a file descriptor index to a file descriptor usable in
   OCaml.  It is the identity on Unix and Mac OS X, and the stdlib
   "win_handle_fd" C stub on Windows. *)
val fd_of_int: int -> Unix.file_descr

(** Separator between paths in PATH environment variable. ":" on Unix/Mac OS X;
    ";" on Windows. *)
val path_separator: string

In the Makefile build system, I used uname to distinguish between these platforms. Perhaps a uname variable in dune would be useful.

I think the following should mostly do it.

For the extension suffix stuff simply lookup the corresponding variables (cf. ocamlc -config | grep ext ).

For the rest simply use ocamlc -config | grep os_type = Win32 to detect windows and let anything else be the value used on all unices.

For which, you should rather use command -v on unices, IIRC which is not available everywhere.

1 Like

Seconded.
The default version of which in Debian (they use something different from GNU which) is even now deprecated: which · d74c1d674fd19e788ab67cad3992cdb3b7cae210 · Debian / debianutils · GitLab

2 Likes

Thank you very much for your answers, @dbuenzli and @kit-ty-kate !

I used the ext_dll variable from ocamlc -config on the latest release of pyml and was happy with that, but a user just reported a strange behavior: with new Apple M1, ext_dll is .so while I would expect .dylib as in previous Mac OS versions. Is it expected? (I should confess I don’t know anything about platform’s conventions!) In any case, I still need to find libpython.dylib, so I will need to rely on another mechanism to detect that pyml is compiled on a Darwin platform…

The behavior that I observe:

tmartinez@MF916-PRO ~ % uname -a                      
Darwin MF916-PRO 20.4.0 Darwin Kernel Version 20.4.0: Thu Apr 22 21:46:41 PDT 2021; root:xnu-7195.101.2~1/RELEASE_ARM64_T8101 arm64
tmartinez@MF916-PRO ~ % ocamlc -config | grep ext     
ext_exe: 
ext_obj: .o
ext_asm: .s
ext_lib: .a
ext_dll: .so

I’m not the owner of an Apple M1 system and not aware if Apple switched its dynamic library conventions on that platform but I find that highly unprobable. Is it maybe an issue with ocaml's configure ?

1 Like

Oh, I made a mistake: ext_dll has always been .so on Mac OS X (even before the switch to autoconf: I checked with OCaml 4.07 and an Intel-based Mac)! So I have to find another mechanism to detect that I need to find a .dylib file.

I believe that the simplest solution is to rely on the result of uname, as @dra27 and @EduardoRFS suggested, and as I did before the switch to dune…

Oh yes, sorry, my bad. That’s what ocaml uses for generating the bytecode dll stublibs and indeed it has always been (somehow wrongly) .so on macOS.

Maybe you could use’s ocamlc -config | grep system = macosx to use .dylib and otherwise use .ext_dll.

1 Like

I would just ask to not use uname, as it makes it painful to cross compile between OS without hacking uname. Use the C compiler to detect this kind of information.

Examples:

detecting android
detecting arm64
detecting Linux, Windows, macOS …

3 Likes

Ok, then ocaml-config:system seems to be reliable indeed to distinguish Windows (with the value win64), Mac OS X (with the value macosx) and any other Unix (whatever the value is, distinct from the two previous ones). Does it sound reasonable and “portable” enough?

@EduardoRFS 's solution looks more general but I believe it is quite overkill for the case of pyml.

Not sure about that. And again, what you want to do on windows likely does not depend on that value (e.g. if you are on a cygwin install).

1 Like

It isn’t reliable enough.

docker run --rm -it  ocaml/opam:windows-mingw-20H2-ocaml-4.12
C:\cygwin64\home\opam>ocamlc -config
...
system: mingw64

@EduardoRFS 's suggestion is the way to go: it will be reliable for you today and will future proof your code for cross compiling.

2 Likes

Thank you a lot everyone! I just made a new release of pyml thanks to your suggestions.

I was reluctant to use dune-configurator since I would try to reduce dependencies, and in particular be able to maintain in parallel the Makefile-based build system (because pyml is used by coccinelle, and coccinelle wants to be compiled without dependencies and in particular without dune).

I replaced @EduardoRFS’s discover program written in OCaml by a simple program directly written in C and which generates the platform-dependent .ml file: I believe this architecture is portable and simple enough to be maintainable.

One thing that I found surprising is that @EduardoRFS’s code only checks for the WIN32 symbol and not _WIN32, whereras with Visual Studio 2017, it appears that only _WIN32 is defined (and, therefore, I checked for both in my code). I don’t know if it is due to the fact that @EduardoRFS use another toolchain (mingw?) or if it is due to something else.

Thank you again a lot, all!

2 Likes

Just in case, here is some wiki page documenting the different possible values depending on the OS: Pre-defined Compiler Macros / Wiki / OperatingSystems

3 Likes

Thank you, @kit-ty-kate! This page seems very useful!

Yes, I only use mingw. But good job finding it.

Your solution is bad for cross compilation tho as you need to execute the target binary. You can do that in pure shell without needing to execute the compiled binary, by using something like echo '#define PLATFORM_NAME X' | cc -dM -E - | grep PLATFORM_NAME | awk '{ print $3 }'

2 Likes