Target platform condition
Introduction
This proposal intends to indroduce a unified way to check building and evironment
conditions and allow to expand them in future. Another intention is to remove redundant os checkes. e.g. all Apple platforms can be checked by #if target(kernel: Darwin)
rather than checking for 4 different OSes.
Motivation
A developer usually must check for environment to expose different behavior in different building situations. In Swift, this usually is done with #if
precompiling directive.
A flow with current approach is we should check every OS separately, this can lead to confusion and increase maintanence cost for currnet codes as Swift is expanding to new environments and a code should be checked against 12 OSes and architectures.
Proposed Solution
This proposal deprecates existing #if arch()
& #if os()
directives and introduces a new way:
#if (type: condition)
The type
can be arch
, os
, kernel
, config
and env
. This design allows to replace:
#if os(macOS) || os(ios) || os(tvOS) || os(watchOS)
simply with a more meaningful:
#if target(kernel: Darwin)
If a new operating system would be introduced by Apple based on Darwin, new code will be compiled correctly without any modification.
Detailed Design
This will conditions available:
Architecture
For arch
type, condition can be x86_64, arm, arm64, i386, powerpc64, powerpc64le and s390x
.
Operating system
For os
type, condition can be macOS, iOS, watchOS, tvOS, GNU, Windows, FreeBSD, Android, PS4, Cygwin, Haiku, Fuchsia
.
Please note Linux is a kernel. GNU fits better as operating system.
Kernel
for kernel
type, condition can be Darwin, Linux, Posix, Zircon
.
Darwin will cover macOS, iOS, watchOS, tvOS
.
Linux will cover GNU, Android, Cygwin
.
BSD will cover FreeBSD, PS4
.
Posix will cover all Darwin, BSD and Linux OSes.
- Windows has it’s own proprietary kernel and defining a separate condition is redundant.
An example of using:
// statvfs(2) doesn't support 64bit inode on Darwin (apfs), fallback to statfs(2)
#if target(kernel: Darwin)
var s = statfs()
guard statfs(path, &s) == 0 else {
throw _NSErrorWithErrno(errno, reading: true, path: path)
}
#elseif (target(kernel: Linux) || target(kernel: BSD)) && !target(os: Android)
// equal with `#elseif target(kernel: Posix) && !target(os: Android)` as we checked Darwin already
var s = statvfs()
guard statvfs(path, &s) == 0 else {
throw _NSErrorWithErrno(errno, reading: true, path: path)
}
#else
NSUnimplemented()
#endif
Environment
For env
type, condition can be simulator, embedded, desktop
.
Also we can define mobile, tablet, watch
as environment, but these conditions should be handled runtime rather than compile time.
This section covers proposal 190.
Configuration
For config
type, condition can be debug, release
. This will be affected by Xcode build configuration.
Endianness
For endian
type, condition can be little, big
. This will be mapped to corresponding arch
types.
Other considerations
We may add support for Metal version by this approach. e.g. #if target(metal: 2)
.
Technically, macOS is platform, Darwin is operating system and XNU is kernel. In proposed system macOS a condition for os
and Darwin as kernel.
Cygwin is considered as operating system. We can define it as env with Windows as operating system. But this may add complexity.
Source compatibility
This is an additive proposal, existing code will continue to work but we can deprecate existing approach.
A warning and fixit may be provided for migrating recognizable cases in existing code, but this will not be necessarily.
Effect on ABI stability
TBD
Effect on API resilience
None
Alternatives Considered
Some possible alternatives can be considered:
- Keep current approach for architecture and os determination.
- Add
#if config()
and#if kernel()
to existing directives. - Using
cpu
as type instead ofarch
.
EDIT: I’ve changed sample to be more clear.