Files
Max DesiatovandGitHub 7b0f469fd4 Add support for Musl libc (#574)
* Add support for Musl libc

Since Musl is sufficiently different from Glibc (see https://wiki.musl-libc.org/functional-differences-from-glibc.html), it requires a different import, which now should be applied to files that have `import Glibc` in them.

* Platform.swift: fix macOS build issue

Glibc and Darwin can share the same `ioctl` code.
2023-06-28 14:02:34 -04:00

161 lines
4.1 KiB
Swift

//===----------------------------------------------------------*- swift -*-===//
//
// This source file is part of the Swift Argument Parser open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//
#if canImport(Glibc)
import Glibc
#elseif canImport(Musl)
import Musl
#elseif canImport(Darwin)
import Darwin
#elseif canImport(CRT)
import CRT
#elseif canImport(WASILibc)
import WASILibc
#endif
enum Platform {}
// MARK: Shell
extension Platform {
/// The name of the user's preferred shell, if detectable from the
/// environment.
static var shellName: String? {
#if os(Windows)
return nil
#else
// FIXME: This retrieves the user's preferred shell, not necessarily the one currently in use.
guard let shellVar = getenv("SHELL") else { return nil }
let shellParts = String(cString: shellVar).split(separator: "/")
return shellParts.last.map(String.init)
#endif
}
}
// MARK: Exit codes
#if os(Windows)
import func WinSDK.GetStdHandle
import func WinSDK.GetConsoleScreenBufferInfo
import let WinSDK.ERROR_BAD_ARGUMENTS
import let WinSDK.STD_OUTPUT_HANDLE
import struct WinSDK.CONSOLE_SCREEN_BUFFER_INFO
#endif
extension Platform {
/// The code for successful exit.
static var exitCodeSuccess: Int32 {
EXIT_SUCCESS
}
/// The code for exit with a general failure.
static var exitCodeFailure: Int32 {
EXIT_FAILURE
}
/// The code for exit with a validation failure.
static var exitCodeValidationFailure: Int32 {
#if os(Windows)
return ERROR_BAD_ARGUMENTS
#elseif os(WASI)
return EXIT_FAILURE
#else
return EX_USAGE
#endif
}
}
// MARK: Exit function
extension Platform {
/// Complete execution with the given exit code.
static func exit(_ code: Int32) -> Never {
#if canImport(Glibc)
Glibc.exit(code)
#elseif canImport(Musl)
Musl.exit(code)
#elseif canImport(Darwin)
Darwin.exit(code)
#elseif canImport(CRT)
ucrt._exit(code)
#elseif canImport(WASILibc)
WASILibc.exit(code)
#endif
}
}
// MARK: Standard error
extension Platform {
/// A type that represents the `stderr` output stream.
struct StandardError: TextOutputStream {
mutating func write(_ string: String) {
for byte in string.utf8 { putc(numericCast(byte), stderr) }
}
}
/// The `stderr` output stream.
static var standardError = StandardError()
}
// MARK: Terminal size
#if canImport(Glibc)
func ioctl(_ a: Int32, _ b: Int32, _ p: UnsafeMutableRawPointer) -> Int32 {
ioctl(CInt(a), UInt(b), p)
}
#endif
extension Platform {
/// The default terminal size.
static var defaultTerminalSize: (width: Int, height: Int) {
(80, 25)
}
/// Returns the current terminal size, or the default if the size is
/// unavailable.
static func terminalSize() -> (width: Int, height: Int) {
#if os(WASI)
// WASI doesn't yet support terminal size
return defaultTerminalSize
#elseif os(Windows)
var csbi: CONSOLE_SCREEN_BUFFER_INFO = CONSOLE_SCREEN_BUFFER_INFO()
guard GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) else {
return defaultTerminalSize
}
return (width: Int(csbi.srWindow.Right - csbi.srWindow.Left) + 1,
height: Int(csbi.srWindow.Bottom - csbi.srWindow.Top) + 1)
#else
var w = winsize()
#if os(OpenBSD)
// TIOCGWINSZ is a complex macro, so we need the flattened value.
let tiocgwinsz = Int32(0x40087468)
let err = ioctl(STDOUT_FILENO, tiocgwinsz, &w)
#elseif canImport(Musl)
let err = ioctl(STDOUT_FILENO, UInt(TIOCGWINSZ), &w)
#else
let err = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w)
#endif
let width = Int(w.ws_col)
let height = Int(w.ws_row)
guard err == 0 else { return defaultTerminalSize }
return (width: width > 0 ? width : defaultTerminalSize.width,
height: height > 0 ? height : defaultTerminalSize.height)
#endif
}
/// The current terminal size, or the default if the width is unavailable.
static var terminalWidth: Int {
terminalSize().width
}
}