Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

system: OS type query #942

Merged
merged 5 commits into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions doc/specs/stdlib_system.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,3 +335,85 @@ Returns a `logical` flag: `.true.` if the system is Windows, or `.false.` otherw
```fortran
{!example/system/example_process_1.f90!}
```

## `get_runtime_os` - Determine the OS type at runtime

### Status

Experimental

### Description

`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.
The supported OS types are `integer, parameter` variables stored in the `stdlib_system` module:

- **Linux** (`OS_LINUX`)
- **macOS** (`OS_MACOS`)
- **Windows** (`OS_WINDOWS`)
- **Cygwin** (`OS_CYGWIN`)
- **Solaris** (`OS_SOLARIS`)
- **FreeBSD** (`OS_FREEBSD`)
- **OpenBSD** (`OS_OPENBSD`)

If the OS cannot be identified, the function returns `OS_UNKNOWN`.

### Syntax

`os = [[stdlib_system(module):get_runtime_os(function)]]()`

### Class

Function

### Arguments

None.

### Return Value

Returns one of the `integer` `OS_*` parameters representing the OS type, from the `stdlib_system` module, or `OS_UNKNOWN` if undetermined.

### Example

```fortran
{!example/system/example_get_runtime_os.f90!}
```

---

## `OS_TYPE` - Cached OS type retrieval

### Status

Experimental

### Description

`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.
Subsequent calls reuse the cached value, making this function highly efficient.

This caching mechanism ensures negligible overhead for repeated calls, unlike `get_runtime_os`, which performs a full runtime inspection.

### Syntax

`os = [[stdlib_system(module):OS_TYPE(function)]]()`

### Class

Function

### Arguments

None.

### Return Value

Returns one of the `integer` `OS_*` parameters representing the OS type, from the `stdlib_system` module, or `OS_UNKNOWN` if undetermined.

---

### Example

```fortran
{!example/system/example_os_type.f90!}
```
2 changes: 2 additions & 0 deletions example/system/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
ADD_EXAMPLE(get_runtime_os)
ADD_EXAMPLE(os_type)
ADD_EXAMPLE(process_1)
ADD_EXAMPLE(process_2)
ADD_EXAMPLE(process_3)
Expand Down
9 changes: 9 additions & 0 deletions example/system/example_get_runtime_os.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
! Demonstrate usage of (non-cached) runtime OS query
program example_get_runtime_os
use stdlib_system, only: OS_NAME, get_runtime_os
implicit none

! Runtime OS detection (full inspection)
print *, "Runtime OS Type: ", OS_NAME(get_runtime_os())

end program example_get_runtime_os
12 changes: 12 additions & 0 deletions example/system/example_os_type.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
! Demonstrate OS detection
program example_os_type
use stdlib_system, only: OS_TYPE, OS_NAME
implicit none

integer :: current_os

! Cached OS detection
current_os = OS_TYPE()
print *, "Current OS Type: ", OS_NAME(current_os)

end program example_os_type
192 changes: 190 additions & 2 deletions src/stdlib_system.F90
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,70 @@ module stdlib_system
private
public :: sleep

!! version: experimental
!!
!! Cached OS type retrieval with negligible runtime overhead.
!! ([Specification](../page/specs/stdlib_system.html#os_type-cached-os-type-retrieval))
!!
!! ### Summary
!! Provides a cached value for the runtime OS type.
!!
!! ### Description
!!
!! This function caches the result of `get_runtime_os` after the first invocation.
!! Subsequent calls return the cached value, ensuring minimal overhead.
!!
public :: OS_TYPE

!! version: experimental
!!
!! Determine the current operating system (OS) type at runtime.
!! ([Specification](../page/specs/stdlib_system.html#get_runtime_os-determine-the-os-type-at-runtime))
!!
!! ### Summary
!! This function inspects the runtime environment to identify the OS type.
!!
!! ### Description
!!
!! The function evaluates environment variables (`OSTYPE` or `OS`) and filesystem attributes
!! to identify the OS. It distinguishes between several common operating systems:
!! - Linux
!! - macOS
!! - Windows
!! - Cygwin
!! - Solaris
!! - FreeBSD
!! - OpenBSD
!!
!! Returns a constant representing the OS type or `OS_UNKNOWN` if the OS cannot be determined.
!!
public :: get_runtime_os

!> Version: experimental
!>
!> Integer constants representing known operating system (OS) types
!> ([Specification](../page/specs/stdlib_system.html))
integer, parameter, public :: &
!> Represents an unknown operating system
OS_UNKNOWN = 0, &
!> Represents a Linux operating system
OS_LINUX = 1, &
!> Represents a macOS operating system
OS_MACOS = 2, &
!> Represents a Windows operating system
OS_WINDOWS = 3, &
!> Represents a Cygwin environment
OS_CYGWIN = 4, &
!> Represents a Solaris operating system
OS_SOLARIS = 5, &
!> Represents a FreeBSD operating system
OS_FREEBSD = 6, &
!> Represents an OpenBSD operating system
OS_OPENBSD = 7

!! Helper function returning the name of an OS parameter
public :: OS_NAME

!> Public sub-processing interface
public :: run
public :: runasync
Expand Down Expand Up @@ -218,7 +282,6 @@ module logical function process_is_running(process) result(is_running)
end function process_is_running
end interface is_running


interface is_completed
!! version: experimental
!!
Expand Down Expand Up @@ -397,7 +460,11 @@ subroutine process_callback(pid,exit_state,stdin,stdout,stderr,payload)
class(*), optional, intent(inout) :: payload
end subroutine process_callback
end interface


!! Static storage for the current OS
logical :: have_os = .false.
integer :: OS_CURRENT = OS_UNKNOWN

interface

!! version: experimental
Expand Down Expand Up @@ -430,4 +497,125 @@ end function process_get_ID

end interface

contains

integer function get_runtime_os() result(os)
!! The function identifies the OS by inspecting environment variables and filesystem attributes.
!!
!! ### Returns:
!! - **OS_UNKNOWN**: If the OS cannot be determined.
!! - **OS_LINUX**, **OS_MACOS**, **OS_WINDOWS**, **OS_CYGWIN**, **OS_SOLARIS**, **OS_FREEBSD**, or **OS_OPENBSD**.
!!
!! Note: This function performs a detailed runtime inspection, so it has non-negligible overhead.

! Local variables
character(len=255) :: val
integer :: length, rc
logical :: file_exists

os = OS_UNKNOWN

! 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
os = OS_LINUX
return

! macOS
elseif (index(val, 'darwin') > 0) then
os = OS_MACOS
return

! Windows, MSYS, MinGW, Git Bash
elseif (index(val, 'win') > 0 .or. index(val, 'msys') > 0) then
os = OS_WINDOWS
return

! Cygwin
elseif (index(val, 'cygwin') > 0) then
os = OS_CYGWIN
return

! Solaris, OpenIndiana, ...
elseif (index(val, 'SunOS') > 0 .or. index(val, 'solaris') > 0) then
os = OS_SOLARIS
return

! FreeBSD
elseif (index(val, 'FreeBSD') > 0 .or. index(val, 'freebsd') > 0) then
os = OS_FREEBSD
return

! OpenBSD
elseif (index(val, 'OpenBSD') > 0 .or. index(val, 'openbsd') > 0) then
os = OS_OPENBSD
return
end if
end if

! 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
os = OS_WINDOWS
return
end if

! Linux
inquire (file='/etc/os-release', exist=file_exists)

if (file_exists) then
os = OS_LINUX
return
end if

! macOS
inquire (file='/usr/bin/sw_vers', exist=file_exists)

if (file_exists) then
os = OS_MACOS
return
end if

! FreeBSD
inquire (file='/bin/freebsd-version', exist=file_exists)

if (file_exists) then
os = OS_FREEBSD
return
end if

end function get_runtime_os

!> Retrieves the cached OS type for minimal runtime overhead.
integer function OS_TYPE() result(os)
!! This function uses a static cache to avoid recalculating the OS type after the first call.
!! It is recommended for performance-sensitive use cases where the OS type is checked multiple times.
if (.not.have_os) then
OS_CURRENT = get_runtime_os()
have_os = .true.
end if
os = OS_CURRENT
end function OS_TYPE

!> Return string describing the OS type flag
pure function OS_NAME(os)
integer, intent(in) :: os
character(len=:), allocatable :: OS_NAME

select case (os)
case (OS_LINUX); OS_NAME = "Linux"
case (OS_MACOS); OS_NAME = "macOS"
case (OS_WINDOWS); OS_NAME = "Windows"
case (OS_CYGWIN); OS_NAME = "Cygwin"
case (OS_SOLARIS); OS_NAME = "Solaris"
case (OS_FREEBSD); OS_NAME = "FreeBSD"
case (OS_OPENBSD); OS_NAME = "OpenBSD"
case default ; OS_NAME = "Unknown"
end select
end function OS_NAME

end module stdlib_system
1 change: 1 addition & 0 deletions test/system/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
ADDTEST(os)
ADDTEST(sleep)
ADDTEST(subprocess)
Loading
Loading