Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to tell what operating system a Fortran executable is running on?

620 views
Skip to first unread message

Clive Page

unread,
Feb 24, 2021, 9:19:06 AM2/24/21
to
I'm writing some software that is designed to run on both Windows and Linux (maybe Macs as well, in due course). It will be compiled using gfortran on each platform. Is there any easy and semi-portable way of determining at run time which operating system the executable is running on? I know I could use something like executing a "dir" command with output to a file, and then read that file, but that's rather slow, cumbersome and indirect.

My initial thought was that using the NEW_LINE intrinsic might work, but that appears to return ASCII 10 (Carriage Return) on both Linux and Windows. The COMPILER_VERSION string gives slightly different results on Windows and Linux, as on the Linux system I normally use, this includes "RedHat" in the string, but that won't be true for all Linux installations I guess.



--
Clive Page

Arjen Markus

unread,
Feb 24, 2021, 10:03:04 AM2/24/21
to
On Wednesday, February 24, 2021 at 3:19:06 PM UTC+1, Clive Page wrote:
> I'm writing some software that is designed to run on both Windows and Linux (maybe Macs as well, in due course). It will be compiled using gfortran on each platform. Is there any easy and semi-portable way of determining at run time which operating system the executable is running on? I know I could use something like executing a "dir" command with output to a file, and then read that file, but that's rather slow, cumbersome and indirect.
>
> My initial thought was that using the NEW_LINE intrinsic might work, but that appears to return ASCII 10 (Carriage Return) on both Linux and Windows. The COMPILER_VERSION string gives slightly different results on Windows and Linux, as on the Linux system I normally use, this includes "RedHat" in the string, but that won't be true for all Linux installations I guess.
>
>
>
The fpm repository contains a file fpm_environment.f90, the function get_os_type in there guesses the opeating system iva various environment variables. Not foolproof, but then it is really messy on Windows ;), with MSYS, MinGW, Cygwin and WSL.

Arjen Markus

unread,
Feb 24, 2021, 10:04:20 AM2/24/21
to
Oh, forgot to mention the location of that repository: https://github.com/fortran-lang/fpm

Regards,

Arjen

Steve Lionel

unread,
Feb 24, 2021, 10:24:26 AM2/24/21
to
On 2/24/2021 9:19 AM, Clive Page wrote:
> I'm writing some software that is designed to run on both Windows and
> Linux (maybe Macs as well, in due course).  It will be compiled using
> gfortran on each platform.  Is there any easy and semi-portable way of
> determining at run time which operating system the executable is running
> on?   I know I could use something like executing a "dir" command with
> output to a file, and then read that file, but that's rather slow,
> cumbersome and indirect.

There is nothing intrinsic in Fortran that would tell you, but you might
consider calling GET_ENVIRONMENT_VARIABLE for "Path" and look for
backslashes that would suggest Windows. Not perfect, I agree, but should
be reasonably reliable. There may be some other clue to distinguish
Linux from Mac.

--
Steve Lionel
ISO/IEC JTC1/SC22/WG5 (Fortran) Convenor
Retired Intel Fortran developer/support
Email: firstname at firstnamelastname dot com
Twitter: @DoctorFortran
LinkedIn: https://www.linkedin.com/in/stevelionel
Blog: https://stevelionel.com/drfortran
WG5: https://wg5-fortran.org

Beliavsky

unread,
Feb 24, 2021, 10:36:33 AM2/24/21
to
I use this function, which gives .true. on Windows Subystem for Linux and .false. for regular Windows.

function unix() result(is_unix)
! test if the operating system is Unix
logical :: is_unix
character (len=100) :: path
call get_environment_variable("PATH",path)
is_unix = path(1:1) == "/"
end function unix

Beliavsky

unread,
Feb 24, 2021, 10:47:09 AM2/24/21
to
On Wednesday, February 24, 2021 at 9:19:06 AM UTC-5, Clive Page wrote:
The program

implicit none
character (len=1000) :: cwd
call getcwd(cwd)
write (*,"(a)") trim(cwd)
end

on Windows using gfortran, g95, and Intel Fortran gives output

c:\fortran\test

for a program run in that directory, and for gfortran on WSL, the result is

/mnt/c/fortran/test

so one can use the first character of the result of a call to getcwd() to determine if the operating system is Unix-like.

Ron Shepard

unread,
Feb 24, 2021, 11:55:23 AM2/24/21
to
There are several approaches to do this, including using the builtin
preprocessor macros for the fortran compiler. I do this with a two-step
procedure. First, I execute the following shell script:

$ cat cte_mod.sh
#!/bin/sh

# shell script to create a *.F90 file with embedded
compile-time-environment information.

# output is written to stdout.
# usage: cte_mod.sh > cte_mod.F90

XsvnX=`svnversion -n`
XdateX=`date`
XhostX=`hostname -s`
XunameX=`uname -sr` # -a is over 132 characters, so keep it short.
XuserX=`whoami`

cat <<EOF | sed \
-e s/XSVNX/"$XsvnX"/ \
-e s/XDATEX/"$XdateX"/ \
-e s/XHOSTX/"$XhostX"/ \
-e s/XUNAMEX/"$XunameX"/ \
-e s/XUSERX/"$XuserX"/
module cte_mod

! this module defines some compile time environment (cte) parameters.

implicit none

! the following constants are defined by the script cte_mod.sh.

character(len=*), parameter :: cte_svn = 'XSVNX' ! svn source
code revision at compile time.
character(len=*), parameter :: cte_date = 'XDATEX' ! compile
date and time.
character(len=*), parameter :: cte_host = 'XHOSTX' ! compile
time hostname.
character(len=*), parameter :: cte_uname = 'XUNAMEX' ! OS info at
compile time.
character(len=*), parameter :: cte_user = 'XUSERX' ! user name
who compiled the code.

contains
subroutine cte_print( nlist )
! print out the entire list of cte* parameters to unit nlist.
implicit none
integer, intent(in) :: nlist
character(len=*), parameter :: cfmt='(2a)'

write(nlist,cfmt) 'cte_svn = ', cte_svn
write(nlist,cfmt) 'cte_date = ', cte_date
write(nlist,cfmt) 'cte_host = ', cte_host
write(nlist,cfmt) 'cte_uname = ', cte_uname
write(nlist,cfmt) 'cte_user = ', cte_user

return
end subroutine cte_print
end module cte_mod

#ifdef DEBUG
! compile with -DDEBUG to create a stand-alone test program.
program cte_test
use cte_mod
implicit none
call cte_print(6)
end program cte_test
#endif
EOF


That shell script then creates a file that I call cte_mod.F90. On this
machine I'm posting from, it looks like this:

$ cat cte_mod.F90
module cte_mod

! this module defines some compile time environment (cte) parameters.

implicit none

! the following constants are defined by the script cte_mod.sh.

character(len=*), parameter :: cte_svn = '4561' ! svn source
code revision at compile time.
character(len=*), parameter :: cte_date = 'Tue Jun 16 01:57:24 CDT
2020' ! compile date and time.
character(len=*), parameter :: cte_host = 'ShepardHome' !
compile time hostname.
character(len=*), parameter :: cte_uname = 'Darwin 17.7.0' ! OS
info at compile time.
character(len=*), parameter :: cte_user = 'shepard' ! user name
who compiled the code.

contains
subroutine cte_print( nlist )
! print out the entire list of cte* parameters to unit nlist.
implicit none
integer, intent(in) :: nlist
character(len=*), parameter :: cfmt='(2a)'

write(nlist,cfmt) 'cte_svn = ', cte_svn
write(nlist,cfmt) 'cte_date = ', cte_date
write(nlist,cfmt) 'cte_host = ', cte_host
write(nlist,cfmt) 'cte_uname = ', cte_uname
write(nlist,cfmt) 'cte_user = ', cte_user

return
end subroutine cte_print
end module cte_mod

#ifdef DEBUG
! compile with -DDEBUG to create a stand-alone test program.
program cte_test
use cte_mod
implicit none
call cte_print(6)
end program cte_test
#endif

You can them compile and USE this module any time you need any of that
information in your program. I think this is POSIX compliant, so it
should be usable on any modern OS, including MacOS. Note that it uses
the "uname" command to determine the OS name and version; you can modify
that to add more OS information. I'm using svn for revision control,
other utilities can be used with minor changes. Other information can be
added to the script and to the module in a straightforward way.

In most codes where I use this routine, the Makefile creates and
compiles a new version of cte_mod.F90 every time the executable is
created. The Makefile contains the following lines to accomplish this.
Note that it uses a phony target to ensure that it is always created.

##
## on every build, save the compile time environment.
##
cte_mod.F90: cte_mod.sh
cte_mod.sh > $@

.PHONY: cte_mod.F90

Ron Shepard

unread,
Feb 24, 2021, 12:13:02 PM2/24/21
to
On 2/24/21 10:55 AM, Ron Shepard wrote:
[...]
> There are several approaches to do this, including using the builtin
> preprocessor macros for the fortran compiler. I do this with a two-step
> procedure. First, I execute the following shell script:

I meant to add this to my previous post.

uname -srm

also tells you if the OS is running in 32-bit or 64-bit mode, so that is
also a useful combination. I think I dropped the -m part a few years ago
when all of the OSs that I use had converted.

$.02 -Ron Shepard

Al Greynolds

unread,
Feb 24, 2021, 4:06:12 PM2/24/21
to
For many years my code has simply checked the "path" environment variable for containing either forward or backward slashes.

Al

Gary Scott

unread,
Feb 24, 2021, 10:38:05 PM2/24/21
to
I would probably try to execute the windows VER command and the unix
uname command. If you get an error code for VER, then try uname. You
can get a whole lot more information from those. Not if you ONLY need
to know unix vs windows (and not VMS, VM, MVS, TSO, OS/8, FLEX, HDOS,
DOS, MSX-DOS, FlexOS, SpartaDOS X, 4690 OS, OS/2, ReactOS, KolibriOS,
SymbOS, DexOS, or some other OS (not that these have Fortran compilers),
then I'll grudgingly agree with checking \ vs / as others have
suggested. My preference would probably be to create a more fully
functional procedure with some options to select specific information to
be extracted.

Gary Scott

unread,
Feb 25, 2021, 9:56:22 AM2/25/21
to
"Not if you ONLY" should be "Now if you ONLY"...

Ev. Drikos

unread,
Feb 25, 2021, 12:39:17 PM2/25/21
to
The OS name is usually known at compile time, unless one compiles ie in
Cygwin. I don't know what happens in WSL.

There are some approaches, ie either the C preprocessor, or a script, or
a ... Java program can determine some values and place them in a file.

BTW, the C preprocessor of gfortran supports some predefined variables
but the OS name isn't one of them. One can use instead a C preprocessing
directive, as gcc defines "__APPLE__", "__linux", "__CYGWIN__" and so on
on the building environment. But gfortran doesn't support them. So this
normally means a C routine.

Below there is a Java class that creates a small Fortran program. Of
course this adds one more compiler dependency. One could do it by hand,
one file per supported system (maybe too much).


Hope this helps,
Ev. Drikos


------------------------------------------------------------------------

gcj --main=jos jos.java -o jos && ./jos > x.F90 && gfc x.F90 && ./a.out

OR

javac jos.java && java jos -cygwin > x.F90 && gfc x.F90 && ./a.out


------------------------------------------------------------------------


class jos {

public jos(){}

public static void main(String argv[]){

String os_name = System.getProperty("os.name");
String os_user = System.getProperty("user.name");
String os_path_sep = System.getProperty("file.separator");
String os_prefix = os_path_sep.equals("/") ? "-" : "/";
String os_home = System.getProperty("user.home");
boolean dynamic = false;

if ( argv != null && argv.length>0 && argv[0] != null) {
if ((argv[0].equalsIgnoreCase("-cygwin") ||
argv[0].equalsIgnoreCase("/cygwin"))) {
dynamic = true;
}}

//One doesn't normally need /var/root as the home path
if ( os_user.equals("root") ) {
}

if ( os_name != null) {
if ( os_name.indexOf("Wind")>=0 && dynamic) {
os_prefix = "";
os_path_sep = "";
os_home = "";
os_name = "";
}
else {
os_home = new String(" ='" + os_home + "'");
os_name = new String(" ='" + os_name + "'");
os_path_sep = new String(" ='" + os_path_sep + "'");
os_prefix = new String(" ='" + os_prefix + "'");
}
}

System.out.println(" character(len=20) :: os_name " + os_name );
System.out.println(" character(len=255) :: homedir " + os_home );
System.out.println(" character(len=1) :: path_sep" + os_path_sep );
System.out.println(" character(len=1) :: prefix " + os_prefix );
System.out.println(" ");

if (dynamic)
System.out.println(" homedir = cygwin_home(os_name,path_sep,prefix) ");

System.out.println(" ");
System.out.println(" print *, ' OS:', os_name ");
System.out.println(" print *, 'Home:', homedir ");
System.out.println(" print *, 'File:', __FILE__ ");
System.out.println(" print *, 'Base File:', __BASE_FILE__ ");
System.out.println(" print *, 'Date:', __DATE__ ");
System.out.println(" print *, 'Time:', __TIME__ ");
System.out.println(" print *, 'TimeStamp:', __TIMESTAMP__ ");
System.out.println(" print *, 'Line:', __LINE__ ");
System.out.println(" print *, 'INCLUDE_LEVEL:', __INCLUDE_LEVEL__ ");
System.out.println(" print *, 'Counter:', __COUNTER__ ");
System.out.println(" ");

if (dynamic) {
System.out.println(" contains ");
System.out.println(" ");
System.out.println(" function
cygwin_home(os_name,path_sep,prefix) result(homedir) ");
System.out.println(" ");
System.out.println(" character(len=20) :: os_name ");
System.out.println(" character(len=255) :: homedir ");
System.out.println(" character(len=1) :: path_sep ");
System.out.println(" character(len=1) :: prefix ");
System.out.println(" character(len=20) :: user ");
System.out.println(" ");
System.out.println(" CALL get_environment_variable('PWD',
path_sep) ");
System.out.println(" !print *, 'path=', path ");
System.out.println(" ");
System.out.println(" if (path_sep == '/') then ");
System.out.println(" os_name = 'CygWin' ");
System.out.println(" CALL get_environment_variable('HOME',
homedir) ");
System.out.println(" prefix = '-'");
System.out.println(" else ");
System.out.println(" os_name = 'Windows' ");
System.out.println(" CALL
get_environment_variable('USERPROFILE', homedir) ");
System.out.println(" path_sep = '\\'");
System.out.println(" prefix = '/'");
System.out.println(" end if ");
System.out.println(" ");
System.out.println(" end ");
System.out.println(" ");
}//if dynamic

System.out.println(" end ");

System.exit(0);
}

}//class jos






Clive Page

unread,
Feb 26, 2021, 4:48:03 AM2/26/21
to
On 24/02/2021 14:19, Clive Page wrote:
> I'm writing some software that is designed to run on both Windows and Linux (maybe Macs as well, in due course).  It will be compiled using gfortran on each platform.  Is there any easy and semi-portable way of determining at run time which operating system the executable is running on?   I know I could use something like executing a "dir" command with output to a file, and then read that file, but that's rather slow, cumbersome and indirect.

Thanks for all the suggestions. Getting the path environment variable and checking its first character seems to work reliably. Alternatively there's a gfortran but non-standard intrinsic GETCWD which gets the current working directory which also works but may not be quite as portable.

--
Clive Page

John

unread,
Feb 27, 2021, 2:30:42 AM2/27/21
to
If you are just looking to tell if you are on an MSWindows machine where a backslash separator will work or is required checking the PATH and HOME variables is probably sufficient, and there are some interesting problems with doing an iINQUIRE of the pathnames './' and '.\' and whether an INQUIRE of ARG0 can be fruitfull, to warn about; (things that work fine with some compilers and not with others) and how if you are only running in your own environments making a "standard" command that handles a lot of system-dependent values using any method for CWD, username, hostname, data and time before f90, jobscheduler information, .... and writes them as a NAMELIST file so any program can call the command and read the output can be useful; but now-adays for all the POSIX platforms you can do most of that with calls to C pretty easily. So it looks like you are good so I will skip all that, but wanted to mention that if your routine might possibly be called a lot that you want to cache and reuse the saved value either with a SAVE or by storing it in a global/module value of one type or another, as interacting with the system with commands, I/O, and environment variables can be quite expensive time-wise.

Assuming you are not doing something more exotic like having a program that has seperate processes running in different environments simultaneously!

This used to be more fun when everyone had their own OS and you wanted to tell VMS from NOS/VE and Primes an VAX and DG and Aegis just using F77! It's getting down-right straight-forward.

Ev. Drikos

unread,
Feb 27, 2021, 3:02:36 AM2/27/21
to
On 27/02/2021 09:30, John wrote:
> If you are just looking to tell if you are on an MSWindows machine where a backslash separator will work or is required checking the PATH and HOME variables is probably sufficient...

Hello,

Examining the PATH isn't sufficient if a program has been compiled in
Cygwin. IMHO, the nix HOME seems to be the USERPROFILEDATA in Windows:
https://gist.github.com/drikosev/b56593217e1cd7657412f8521cac9d63

Simon Geard

unread,
Feb 27, 2021, 4:47:23 AM2/27/21
to
Personally I'd avoid looking at environment variables, instead I'd try
to read the (virtual) file /proc/version

If it doesn't exist then you're on Windows.
If it does you need to look at the string, on WSL it contains
'Microsoft' so you're still on Windows
You might need to do a similar test for Cygwin
This should work on all Unix systems so you should be able to detect
them as well.

Simon

Ev. Drikos

unread,
Feb 27, 2021, 4:48:49 AM2/27/21
to
On 27/02/2021 10:02, Ev. Drikos wrote:
> On 27/02/2021 09:30, John wrote:
>> ... IMHO, the nix HOME seems to be the USERPROFILEDATA in Windows:
> https://gist.github.com/drikosev/b56593217e1cd7657412f8521cac9d63
>
IMHO, the nix HOME seems to be the *USERPROFILE* in Windows:

Ev. Drikos

unread,
Feb 27, 2021, 5:02:16 AM2/27/21
to
On 27/02/2021 11:47, Simon Geard wrote:
> On 27/02/2021 08:02, Ev. Drikos wrote:
>>
>> Examining the PATH isn't sufficient if a program has been compiled in
>> Cygwin. IMHO, the nix HOME seems to be the USERPROFILE in Windows:
>> https://gist.github.com/drikosev/b56593217e1cd7657412f8521cac9d63
>>
>
> Personally I'd avoid looking at environment variables, instead I'd try
> to read the (virtual) file /proc/version
In example, there isn't such a file in macOS.

> If it does you need to look at the string, on WSL it contains
> 'Microsoft' so you're still on Windows
> You might need to do a similar test for Cygwin

The OS Name in Cygwin doesn't help. My question is whether PWD is
credible in WSL, as it isn't credible ie in OS X if a user runs sudo.

Thank you,
Ev. Drikos

Simon Geard

unread,
Feb 27, 2021, 6:15:31 AM2/27/21
to
On 27/02/2021 10:02, Ev. Drikos wrote:
In WSL you get a Linux environment so
echo $SHELL -> /bin/bash
pwd -> /home/simon
uname -s -> Linux
/proc/version -> exists

On a Mac
echo $SHELL -> /bin/bash
pwd -> /home/simon
uname -s -> Darwin
/proc/version -> does not exist

Simon

Ron Shepard

unread,
Feb 27, 2021, 11:12:43 AM2/27/21
to
Yes, the /proc stuff, as handy as it is, is linux specific, not a subset
of the more portable POSIX environment. As I posted before, the POSIX
uname command with the appropriate options is probably the best approach
for the information. Start with "uname -a" to see what all is there, and
then shorten it for your purposes as required. As for runtime
efficiency, it is impossible to be any more efficient than referencing a
fortran parameter in a module, there is no OS runtime overhead
associated with that at all. Code to do all of this was posted
previously. It is stable and reliable, I have not touched it in years. I
don't program in MS Windows, but it claims to be POSIX compliant, so it
should all just work.

$.02 -Ron Shepard


Ev. Drikos

unread,
Feb 28, 2021, 12:39:42 AM2/28/21
to
On 27/02/2021 18:12, Ron Shepard wrote:
> ...
>
Hello,

IMHO, most of your arguments are correct.

> ... As I posted before, the POSIX
> uname command with the appropriate options is probably the best approach
> for the information...

Even if one cross compiles?

Ev. Drikos

Ron Shepard

unread,
Feb 28, 2021, 3:34:52 AM2/28/21
to
That is a good point. My code saves the compile time environment into
the module. If you are cross compiling, then you would need to put the
target machine information into a module in a different way. If you are
compiling an executable that will run on several machines, and if you
are interested in the runtime environment, then you must query the OS at
run time and pay the extra costs that that requires.

$.02 -Ron Shepard

Ev. Drikos

unread,
Feb 28, 2021, 11:02:28 AM2/28/21
to
On 27/02/2021 11:48, Ev. Drikos wrote:
> On 27/02/2021 10:02, Ev. Drikos wrote:
>> On 27/02/2021 09:30, John wrote:
>>> ...  IMHO, the nix HOME seems to be the USERPROFILEDATA in Windows:
>> https://gist.github.com/drikosev/b56593217e1cd7657412f8521cac9d63
>> ...

Finally, I combined various ideas I read in this thread and revised:
https://gist.github.com/drikosev/c5b650e8cf2e0ec68d9ba193c6ace47f

Lynn McGuire

unread,
Mar 1, 2021, 9:54:07 PM3/1/21
to
On 2/24/2021 8:19 AM, Clive Page wrote:
If you are using the C preprocessor in Fortran:

http://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor

Lynn


0 new messages