1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
|
!> This module contains procedures that interact with the programming environment.
!!
!! * [get_os_type] -- Determine the OS type
!! * [get_env] -- return the value of an environment variable
module fpm_environment
use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, &
& stdout=>output_unit, &
& stderr=>error_unit
use fpm_error, only : fpm_stop
implicit none
private
public :: get_os_type
public :: os_is_unix
public :: get_env
public :: get_command_arguments_quoted
public :: separator
public :: os_is_32bit_windows
integer, parameter, public :: OS_UNKNOWN = 0
integer, parameter, public :: OS_LINUX = 1
integer, parameter, public :: OS_MACOS = 2
integer, parameter, public :: OS_WINDOWS = 3
integer, parameter, public :: OS_CYGWIN = 4
integer, parameter, public :: OS_SOLARIS = 5
integer, parameter, public :: OS_FREEBSD = 6
integer, parameter, public :: OS_OPENBSD = 7
contains
!> Determine the OS type
integer function get_os_type() result(r)
!!
!! Returns one of OS_UNKNOWN, OS_LINUX, OS_MACOS, OS_WINDOWS, OS_CYGWIN,
!! OS_SOLARIS, OS_FREEBSD, OS_OPENBSD.
!!
!! At first, the environment variable `OS` is checked, which is usually
!! found on Windows. Then, `OSTYPE` is read in and compared with common
!! names. If this fails too, check the existence of files that can be
!! found on specific system types only.
!!
!! Returns OS_UNKNOWN if the operating system cannot be determined.
character(len=32) :: val
integer :: length, rc
logical :: file_exists
logical, save :: first_run = .true.
integer, save :: ret = OS_UNKNOWN
!omp threadprivate(ret, first_run)
if (.not. first_run) then
r = ret
return
end if
first_run = .false.
r = OS_UNKNOWN
! Check environment variable `OS`.
call get_environment_variable('OS', val, length, rc)
if (rc == 0 .and. length > 0 .and. index(val, 'Windows_NT') > 0) then
r = OS_WINDOWS
ret = r
return
end if
! Check environment variable `OSTYPE`.
call get_environment_variable('OSTYPE', val, length, rc)
if (rc == 0 .and. length > 0) then
! Linux
if (index(val, 'linux') > 0) then
r = OS_LINUX
ret = r
return
end if
! macOS
if (index(val, 'darwin') > 0) then
r = OS_MACOS
ret = r
return
end if
! Windows, MSYS, MinGW, Git Bash
if (index(val, 'win') > 0 .or. index(val, 'msys') > 0) then
r = OS_WINDOWS
ret = r
return
end if
! Cygwin
if (index(val, 'cygwin') > 0) then
r = OS_CYGWIN
ret = r
return
end if
! Solaris, OpenIndiana, ...
if (index(val, 'SunOS') > 0 .or. index(val, 'solaris') > 0) then
r = OS_SOLARIS
ret = r
return
end if
! FreeBSD
if (index(val, 'FreeBSD') > 0 .or. index(val, 'freebsd') > 0) then
r = OS_FREEBSD
ret = r
return
end if
! OpenBSD
if (index(val, 'OpenBSD') > 0 .or. index(val, 'openbsd') > 0) then
r = OS_OPENBSD
ret = r
return
end if
end if
! Linux
inquire (file='/etc/os-release', exist=file_exists)
if (file_exists) then
r = OS_LINUX
ret = r
return
end if
! macOS
inquire (file='/usr/bin/sw_vers', exist=file_exists)
if (file_exists) then
r = OS_MACOS
ret = r
return
end if
! FreeBSD
inquire (file='/bin/freebsd-version', exist=file_exists)
if (file_exists) then
r = OS_FREEBSD
ret = r
return
end if
end function get_os_type
!> Compare the output of [[get_os_type]] or the optional
!! passed INTEGER value to the value for OS_WINDOWS
!! and return .TRUE. if they match and .FALSE. otherwise
logical function os_is_unix(os) result(unix)
integer, intent(in), optional :: os
integer :: build_os
if (present(os)) then
build_os = os
else
build_os = get_os_type()
end if
unix = build_os /= OS_WINDOWS
end function os_is_unix
logical function os_is_32bit_windows(os) result(win_32bit)
use fpm_os, only: windows_is_wow64
implicit none
integer, intent(in), optional :: os
integer :: build_os
if (present(os)) then
build_os = os
else
build_os = get_os_type()
end if
if(build_os == OS_WINDOWS) then
win_32bit = .not. windows_is_wow64()
else
win_32bit = .false.
end if
end function os_is_32bit_windows
!> get named environment variable value. It it is blank or
!! not set return the optional default value
function get_env(NAME,DEFAULT) result(VALUE)
implicit none
!> name of environment variable to get the value of
character(len=*),intent(in) :: NAME
!> default value to return if the requested value is undefined or blank
character(len=*),intent(in),optional :: DEFAULT
!> the returned value
character(len=:),allocatable :: VALUE
integer :: howbig
integer :: stat
integer :: length
! get length required to hold value
length=0
if(NAME.ne.'')then
call get_environment_variable(NAME, length=howbig,status=stat,trim_name=.true.)
select case (stat)
case (1)
!*!print *, NAME, " is not defined in the environment. Strange..."
VALUE=''
case (2)
!*!print *, "This processor doesn't support environment variables. Boooh!"
VALUE=''
case default
! make string to hold value of sufficient size
allocate(character(len=max(howbig,1)) :: VALUE)
! get value
call get_environment_variable(NAME,VALUE,status=stat,trim_name=.true.)
if(stat.ne.0)VALUE=''
end select
else
VALUE=''
endif
if(VALUE.eq.''.and.present(DEFAULT))VALUE=DEFAULT
end function get_env
function get_command_arguments_quoted() result(args)
character(len=:),allocatable :: args
character(len=:),allocatable :: arg
character(len=1) :: quote
integer :: ilength, istatus, i
ilength=0
args=''
quote=merge('"',"'",separator().eq.'\')
do i=2,command_argument_count() ! look at all arguments after subcommand
call get_command_argument(number=i,length=ilength,status=istatus)
if(istatus /= 0) then
write(stderr,'(*(g0,1x))')'<ERROR>*get_command_arguments_stack* error obtaining argument ',i
exit
else
if(allocated(arg))deallocate(arg)
allocate(character(len=ilength) :: arg)
call get_command_argument(number=i,value=arg,length=ilength,status=istatus)
if(istatus /= 0) then
write(stderr,'(*(g0,1x))')'<ERROR>*get_command_arguments_stack* error obtaining argument ',i
exit
elseif(ilength.gt.0)then
if(index(arg//' ','-').ne.1)then
args=args//quote//arg//quote//' '
elseif(index(arg,' ').ne.0)then
args=args//quote//arg//quote//' '
else
args=args//arg//' '
endif
else
args=args//repeat(quote,2)//' '
endif
endif
enddo
end function get_command_arguments_quoted
function separator() result(sep)
!>
!!##NAME
!! separator(3f) - [M_io:ENVIRONMENT] try to determine pathname directory separator character
!! (LICENSE:PD)
!!
!!##SYNOPSIS
!!
!! function separator() result(sep)
!!
!! character(len=1) :: sep
!!
!!##DESCRIPTION
!! First using the name the program was invoked with, then the name
!! returned by an INQUIRE(3f) of that name, then ".\NAME" and "./NAME"
!! try to determine the separator character used to separate directory
!! names from file basenames.
!!
!! If a slash or backslash is not found in the name, the environment
!! variable PATH is examined first for a backslash, then a slash.
!!
!! Can be very system dependent. If the queries fail the default returned
!! is "/".
!!
!!##EXAMPLE
!!
!! sample usage
!!
!! program demo_separator
!! use M_io, only : separator
!! implicit none
!! write(*,*)'separator=',separator()
!! end program demo_separator
! use the pathname returned as arg0 to determine pathname separator
implicit none
character(len=:),allocatable :: arg0
integer :: arg0_length
integer :: istat
logical :: existing
character(len=1) :: sep
!*ifort_bug*!character(len=1),save :: sep_cache=' '
character(len=4096) :: name
character(len=:),allocatable :: fname
!*ifort_bug*! if(sep_cache.ne.' ')then ! use cached value. NOTE: A parallel code might theoretically use multiple OS
!*ifort_bug*! sep=sep_cache
!*ifort_bug*! return
!*ifort_bug*! endif
arg0_length=0
name=' '
call get_command_argument(0,length=arg0_length,status=istat)
if(allocated(arg0))deallocate(arg0)
allocate(character(len=arg0_length) :: arg0)
call get_command_argument(0,arg0,status=istat)
! check argument name
if(index(arg0,'\').ne.0)then
sep='\'
elseif(index(arg0,'/').ne.0)then
sep='/'
else
! try name returned by INQUIRE(3f)
existing=.false.
name=' '
inquire(file=arg0,iostat=istat,exist=existing,name=name)
if(index(name,'\').ne.0)then
sep='\'
elseif(index(name,'/').ne.0)then
sep='/'
else
! well, try some common syntax and assume in current directory
fname='.\'//arg0
inquire(file=fname,iostat=istat,exist=existing)
if(existing)then
sep='\'
else
fname='./'//arg0
inquire(file=fname,iostat=istat,exist=existing)
if(existing)then
sep='/'
else ! check environment variable PATH
sep=merge('\','/',index(get_env('PATH'),'\').ne.0)
!*!write(*,*)'<WARNING>unknown system directory path separator'
endif
endif
endif
endif
!*ifort_bug*!sep_cache=sep
end function separator
end module fpm_environment
|