I distribute various versions of an executable file to various people for testing and comment. The versions are distinguished by the name of the file, e.g. "testfile_1a.exe", testfile_2b.exe", etc. The program produces a text file output. I would like the first line of this text file to contain the name of the executable that produced it (so that when they return with their sample output I can tell how they actually got it). Is this possible? I cannot find an intrinsic or extended Fortran function that does this. Perhaps an environment variable?
You can call GETARG(0,..), but you will not have any protection from your users changing the EXE name (for any number of reasons, including not wanting to have a long name, not having a name that is already used for another file, etc).
Thanks for the suggestions, I will check them out.
The reason I do not code a version ID in the source file is it's "dangerous": It's too easy to forget to update it when preparing a new version, or worse yet to revert it to a previous version when I want to make a minor-minor change in that. Also, it's VERY beneficial for the users/testers to SEE which particular exe file version they want to run without waiting for the output file to tell them they chose the right or wrong one.
So, its best (for this case anyway) to code the version into the file name, and make it automatic to appear correctly in the output.
Of course users CAN change the file name (accidentally or maliciously or with good intentions), so it may not be a good system in general.
The best way to do this on Windows is:
1. Call the Windows API routine GetModuleHandle , passing a NULL argument. This gets you the handle to the current EXE
2. Call GetModuleFileName on the returned handle.
Yes, perhaps the best way in Windows, but how portable is it? Eventually, the program will need to work with other compilers and other operating systems, including Linux and (maybe even!) Unix. It's a huge code, been around for many years, has had various coders work on it, and all have been very careful to use only intrinsic Fortran operations.
There is no truly portable way to do this. GET_COMMAND_ARGUMENT (and the nonstandard GETARG) will work sometimes, but "argument 0" is specified as being the "command" that invoked the program, not necessarily the name or path to the executable.
There is a way that's supposed to be portable using C interoperability. Suppose we create a file version.txt:
This is version 1.0.1
We can use the objcopy utility that comes with gfortran to make this into an object file:
D:\>objcopy --input binary --output pe-x86-64 --binary-architecture i386:x86-64 version.txt version.o
This is suitable for Windows x64 programs. Now we can write a program that uses C interoperability to read the embedded data:
module M use ISO_C_BINDING implicit none character(KIND=C_CHAR), bind(C,name='_binary_version_txt_start'), target :: version_start integer(C_INT), bind(C,name='_binary_version_txt_size'), target :: version_len end module M program P use ISO_C_BINDING use M implicit none character(LEN=:,KIND=C_CHAR), pointer :: version type(C_PTR) version_ptr, len_ptr version_ptr = C_LOC(version_start) len_ptr = C_LOC(version_len) write(*,*) transfer(version_ptr,0_C_INTPTR_T) write(*,*) transfer(len_ptr,0_C_INTPTR_T) BLOCK character(LEN=transfer(len_ptr,0_C_INTPTR_T),KIND=C_CHAR), pointer :: temp call C_F_POINTER(version_ptr,temp) version=>temp END BLOCK write(*,'(a)') version end program P
Now we can link this with the version.o file we made earlier:
D:\>gfortran P.f90 version.o -oP D:\>P 4206608 23 This is version 1.0.1
So we get our embedded data back. Unfortunately ifort+link.exe doesn't feel the same way about this as ld.exe does; I don't know whether that's a problem with the linkers or with the Fortran compilers. It seems reasonable enough, though, and gfortran likes it OK, so I don't know what the problem is with ifort. I guess we might be able to work around this situation with _binary_version_txt_end instead:
module M use ISO_C_BINDING implicit none character(KIND=C_CHAR), bind(C,name='_binary_version_txt_start'), target :: version_start character(KIND=C_CHAR), bind(C,name='_binary_version_txt_end'), target :: version_end end module M program P use ISO_C_BINDING use M implicit none character(LEN=:,KIND=C_CHAR), pointer :: version type(C_PTR) version_ptr integer(C_INTPTR_T) version_len version_ptr = C_LOC(version_start) version_len = transfer(C_LOC(version_end),0_C_INTPTR_T)-transfer(version_ptr,0_C_INTPTR_T) write(*,*) transfer(version_ptr,0_C_INTPTR_T) write(*,*) version_len BLOCK character(LEN=version_len,KIND=C_CHAR), pointer :: temp call C_F_POINTER(version_ptr,temp) version=>temp END BLOCK write(*,'(a)') version end program P
Now, I get the same result with
D:\>gfortran P2.f90 version.o -oP2 D:\>P2 4206608 23 This is version 1.0.1
And a positive result this time with ifort:
D:\>ifort /nologo P2.f90 version.o ifort: command line warning #10161: unrecognized source type 'version.o'; object file assumed D:\>P2 140699452588044 23 This is version 1.0.1
So it looks like this should be portable to anything gcc has been ported to which may be a superset of platforms supporting Fortran 2003.
The windows function GetCommandLine returns the execution command completed with the full path of the .exe module.
This feature is quite useful but is not implemented in the Fortran function GET_COMMAND or GET_COMMAND_ARGUMENT.
I have been experimenting with two intrinsic routines, GETARG (0, buffer) and GET_COMMAND (buffer). Both of these return the entire path spec of the command in buffer. The documentation is not very clear on this. I prefer to get the command only, so I use SPLITPATHQQ to get that.
The suggested methods using Windows API and C interoperability seem way too much trouble, at least for my current casual need.
And just to be clear, routines GET_COMMAND and GET_COMMAND_ARGUMENT are standard. GETARG appears to be a nonstandard equivalent to GET_COMMAND_ARGUMENT.