Skip to content

Commit d724658

Browse files
authored
system: OS type query (#942)
2 parents d2767cf + fbaacc9 commit d724658

File tree

7 files changed

+366
-2
lines changed

7 files changed

+366
-2
lines changed

doc/specs/stdlib_system.md

+82
Original file line numberDiff line numberDiff line change
@@ -335,3 +335,85 @@ Returns a `logical` flag: `.true.` if the system is Windows, or `.false.` otherw
335335
```fortran
336336
{!example/system/example_process_1.f90!}
337337
```
338+
339+
## `get_runtime_os` - Determine the OS type at runtime
340+
341+
### Status
342+
343+
Experimental
344+
345+
### Description
346+
347+
`get_runtime_os` inspects the runtime environment to identify the current OS type. It evaluates environment variables (`OSTYPE`, `OS`) and checks for specific files associated with known operating systems.
348+
The supported OS types are `integer, parameter` variables stored in the `stdlib_system` module:
349+
350+
- **Linux** (`OS_LINUX`)
351+
- **macOS** (`OS_MACOS`)
352+
- **Windows** (`OS_WINDOWS`)
353+
- **Cygwin** (`OS_CYGWIN`)
354+
- **Solaris** (`OS_SOLARIS`)
355+
- **FreeBSD** (`OS_FREEBSD`)
356+
- **OpenBSD** (`OS_OPENBSD`)
357+
358+
If the OS cannot be identified, the function returns `OS_UNKNOWN`.
359+
360+
### Syntax
361+
362+
`os = [[stdlib_system(module):get_runtime_os(function)]]()`
363+
364+
### Class
365+
366+
Function
367+
368+
### Arguments
369+
370+
None.
371+
372+
### Return Value
373+
374+
Returns one of the `integer` `OS_*` parameters representing the OS type, from the `stdlib_system` module, or `OS_UNKNOWN` if undetermined.
375+
376+
### Example
377+
378+
```fortran
379+
{!example/system/example_get_runtime_os.f90!}
380+
```
381+
382+
---
383+
384+
## `OS_TYPE` - Cached OS type retrieval
385+
386+
### Status
387+
388+
Experimental
389+
390+
### Description
391+
392+
`OS_TYPE` provides a cached result of the `get_runtime_os` function. The OS type is determined during the first invocation and stored in a static variable.
393+
Subsequent calls reuse the cached value, making this function highly efficient.
394+
395+
This caching mechanism ensures negligible overhead for repeated calls, unlike `get_runtime_os`, which performs a full runtime inspection.
396+
397+
### Syntax
398+
399+
`os = [[stdlib_system(module):OS_TYPE(function)]]()`
400+
401+
### Class
402+
403+
Function
404+
405+
### Arguments
406+
407+
None.
408+
409+
### Return Value
410+
411+
Returns one of the `integer` `OS_*` parameters representing the OS type, from the `stdlib_system` module, or `OS_UNKNOWN` if undetermined.
412+
413+
---
414+
415+
### Example
416+
417+
```fortran
418+
{!example/system/example_os_type.f90!}
419+
```

example/system/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
ADD_EXAMPLE(get_runtime_os)
2+
ADD_EXAMPLE(os_type)
13
ADD_EXAMPLE(process_1)
24
ADD_EXAMPLE(process_2)
35
ADD_EXAMPLE(process_3)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
! Demonstrate usage of (non-cached) runtime OS query
2+
program example_get_runtime_os
3+
use stdlib_system, only: OS_NAME, get_runtime_os
4+
implicit none
5+
6+
! Runtime OS detection (full inspection)
7+
print *, "Runtime OS Type: ", OS_NAME(get_runtime_os())
8+
9+
end program example_get_runtime_os

example/system/example_os_type.f90

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
! Demonstrate OS detection
2+
program example_os_type
3+
use stdlib_system, only: OS_TYPE, OS_NAME
4+
implicit none
5+
6+
integer :: current_os
7+
8+
! Cached OS detection
9+
current_os = OS_TYPE()
10+
print *, "Current OS Type: ", OS_NAME(current_os)
11+
12+
end program example_os_type

src/stdlib_system.F90

+190-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,70 @@ module stdlib_system
55
private
66
public :: sleep
77

8+
!! version: experimental
9+
!!
10+
!! Cached OS type retrieval with negligible runtime overhead.
11+
!! ([Specification](../page/specs/stdlib_system.html#os_type-cached-os-type-retrieval))
12+
!!
13+
!! ### Summary
14+
!! Provides a cached value for the runtime OS type.
15+
!!
16+
!! ### Description
17+
!!
18+
!! This function caches the result of `get_runtime_os` after the first invocation.
19+
!! Subsequent calls return the cached value, ensuring minimal overhead.
20+
!!
21+
public :: OS_TYPE
22+
23+
!! version: experimental
24+
!!
25+
!! Determine the current operating system (OS) type at runtime.
26+
!! ([Specification](../page/specs/stdlib_system.html#get_runtime_os-determine-the-os-type-at-runtime))
27+
!!
28+
!! ### Summary
29+
!! This function inspects the runtime environment to identify the OS type.
30+
!!
31+
!! ### Description
32+
!!
33+
!! The function evaluates environment variables (`OSTYPE` or `OS`) and filesystem attributes
34+
!! to identify the OS. It distinguishes between several common operating systems:
35+
!! - Linux
36+
!! - macOS
37+
!! - Windows
38+
!! - Cygwin
39+
!! - Solaris
40+
!! - FreeBSD
41+
!! - OpenBSD
42+
!!
43+
!! Returns a constant representing the OS type or `OS_UNKNOWN` if the OS cannot be determined.
44+
!!
45+
public :: get_runtime_os
46+
47+
!> Version: experimental
48+
!>
49+
!> Integer constants representing known operating system (OS) types
50+
!> ([Specification](../page/specs/stdlib_system.html))
51+
integer, parameter, public :: &
52+
!> Represents an unknown operating system
53+
OS_UNKNOWN = 0, &
54+
!> Represents a Linux operating system
55+
OS_LINUX = 1, &
56+
!> Represents a macOS operating system
57+
OS_MACOS = 2, &
58+
!> Represents a Windows operating system
59+
OS_WINDOWS = 3, &
60+
!> Represents a Cygwin environment
61+
OS_CYGWIN = 4, &
62+
!> Represents a Solaris operating system
63+
OS_SOLARIS = 5, &
64+
!> Represents a FreeBSD operating system
65+
OS_FREEBSD = 6, &
66+
!> Represents an OpenBSD operating system
67+
OS_OPENBSD = 7
68+
69+
!! Helper function returning the name of an OS parameter
70+
public :: OS_NAME
71+
872
!> Public sub-processing interface
973
public :: run
1074
public :: runasync
@@ -218,7 +282,6 @@ module logical function process_is_running(process) result(is_running)
218282
end function process_is_running
219283
end interface is_running
220284

221-
222285
interface is_completed
223286
!! version: experimental
224287
!!
@@ -397,7 +460,11 @@ subroutine process_callback(pid,exit_state,stdin,stdout,stderr,payload)
397460
class(*), optional, intent(inout) :: payload
398461
end subroutine process_callback
399462
end interface
400-
463+
464+
!! Static storage for the current OS
465+
logical :: have_os = .false.
466+
integer :: OS_CURRENT = OS_UNKNOWN
467+
401468
interface
402469

403470
!! version: experimental
@@ -430,4 +497,125 @@ end function process_get_ID
430497

431498
end interface
432499

500+
contains
501+
502+
integer function get_runtime_os() result(os)
503+
!! The function identifies the OS by inspecting environment variables and filesystem attributes.
504+
!!
505+
!! ### Returns:
506+
!! - **OS_UNKNOWN**: If the OS cannot be determined.
507+
!! - **OS_LINUX**, **OS_MACOS**, **OS_WINDOWS**, **OS_CYGWIN**, **OS_SOLARIS**, **OS_FREEBSD**, or **OS_OPENBSD**.
508+
!!
509+
!! Note: This function performs a detailed runtime inspection, so it has non-negligible overhead.
510+
511+
! Local variables
512+
character(len=255) :: val
513+
integer :: length, rc
514+
logical :: file_exists
515+
516+
os = OS_UNKNOWN
517+
518+
! Check environment variable `OSTYPE`.
519+
call get_environment_variable('OSTYPE', val, length, rc)
520+
521+
if (rc == 0 .and. length > 0) then
522+
! Linux
523+
if (index(val, 'linux') > 0) then
524+
os = OS_LINUX
525+
return
526+
527+
! macOS
528+
elseif (index(val, 'darwin') > 0) then
529+
os = OS_MACOS
530+
return
531+
532+
! Windows, MSYS, MinGW, Git Bash
533+
elseif (index(val, 'win') > 0 .or. index(val, 'msys') > 0) then
534+
os = OS_WINDOWS
535+
return
536+
537+
! Cygwin
538+
elseif (index(val, 'cygwin') > 0) then
539+
os = OS_CYGWIN
540+
return
541+
542+
! Solaris, OpenIndiana, ...
543+
elseif (index(val, 'SunOS') > 0 .or. index(val, 'solaris') > 0) then
544+
os = OS_SOLARIS
545+
return
546+
547+
! FreeBSD
548+
elseif (index(val, 'FreeBSD') > 0 .or. index(val, 'freebsd') > 0) then
549+
os = OS_FREEBSD
550+
return
551+
552+
! OpenBSD
553+
elseif (index(val, 'OpenBSD') > 0 .or. index(val, 'openbsd') > 0) then
554+
os = OS_OPENBSD
555+
return
556+
end if
557+
end if
558+
559+
! Check environment variable `OS`.
560+
call get_environment_variable('OS', val, length, rc)
561+
562+
if (rc == 0 .and. length > 0 .and. index(val, 'Windows_NT') > 0) then
563+
os = OS_WINDOWS
564+
return
565+
end if
566+
567+
! Linux
568+
inquire (file='/etc/os-release', exist=file_exists)
569+
570+
if (file_exists) then
571+
os = OS_LINUX
572+
return
573+
end if
574+
575+
! macOS
576+
inquire (file='/usr/bin/sw_vers', exist=file_exists)
577+
578+
if (file_exists) then
579+
os = OS_MACOS
580+
return
581+
end if
582+
583+
! FreeBSD
584+
inquire (file='/bin/freebsd-version', exist=file_exists)
585+
586+
if (file_exists) then
587+
os = OS_FREEBSD
588+
return
589+
end if
590+
591+
end function get_runtime_os
592+
593+
!> Retrieves the cached OS type for minimal runtime overhead.
594+
integer function OS_TYPE() result(os)
595+
!! This function uses a static cache to avoid recalculating the OS type after the first call.
596+
!! It is recommended for performance-sensitive use cases where the OS type is checked multiple times.
597+
if (.not.have_os) then
598+
OS_CURRENT = get_runtime_os()
599+
have_os = .true.
600+
end if
601+
os = OS_CURRENT
602+
end function OS_TYPE
603+
604+
!> Return string describing the OS type flag
605+
pure function OS_NAME(os)
606+
integer, intent(in) :: os
607+
character(len=:), allocatable :: OS_NAME
608+
609+
select case (os)
610+
case (OS_LINUX); OS_NAME = "Linux"
611+
case (OS_MACOS); OS_NAME = "macOS"
612+
case (OS_WINDOWS); OS_NAME = "Windows"
613+
case (OS_CYGWIN); OS_NAME = "Cygwin"
614+
case (OS_SOLARIS); OS_NAME = "Solaris"
615+
case (OS_FREEBSD); OS_NAME = "FreeBSD"
616+
case (OS_OPENBSD); OS_NAME = "OpenBSD"
617+
case default ; OS_NAME = "Unknown"
618+
end select
619+
end function OS_NAME
620+
433621
end module stdlib_system

test/system/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
ADDTEST(os)
12
ADDTEST(sleep)
23
ADDTEST(subprocess)

0 commit comments

Comments
 (0)