Currently, cross-platform Swift programs that rely on symbols defined in
libc (`fputs`, `stderr`, etc.) must all write the same five lines of
#if os(Linux) || os(FreeBSD)
Instead, I propose the following, which will work on all platforms:
Let's say we wanted to write a program that, on any platform, would print
"Hello world!" to stderr. We'd probably come up with this:
#if os(Linux) || os(FreeBSD)
fputs("Hello world!", stderr)
The first five lines of this program are necessary to import the symbols
`fputs` and `stderr`. Five lines may not be much, but these come with
- They must be written in each source file that relies on libc, which is
- It is subject to frequent change. As Swift is ported to more platforms,
that initial check must change to `#if os(Linux) || os(FreeBSD) ||
os(Windows) || os(Android)`, and so on. End users of Swift may not be
actively involved in its development, and so may be surprised when the
latest release suddenly necessitates more `os()` conditions.
- These combined force users to make a conscious decision to write
cross-platform code--as opposed to simply writing Swift and have it work on
other platforms seamlessly.
It would be preferable if people writing Swift did not need to check for
the current `os()` in order to write code that works across platforms.
# Proposed solution
Instead of conditionally importing Darwin or Glibc, I propose the following:
This would import whichever libc implementation Swift was compiled with.
For Ubuntu Linux releases, this would be Glibc. For OS X releases, this
would be Darwin. For Android (coming soon in
https://github.com/apple/swift/pull/1442), this would be Bionic.
This saves the end user from writing boilerplate code, and it isolates them
from the rapid expansion of platforms on which Swift is able to be executed.
This idea is not novel: the Swift package manager already defines a "libc"
package that is essentially the boilerplate `os()` check above:
However, rather than determining which libc implementation to use at
runtime (like SwiftPM does above), I propose we allow the Swift stdlib to
be compiled with any arbitrary implementation of libc.
# Detailed design
It's my understanding that the majority of this change would take place in
the Swift build scripts and CMake modules. Similar to how those scripts
export a module named "Glibc" on Linux (see stdlib/public/core/Glibc), this
proposal could be implementing by exporting a "Libc" on all platforms.
This would also be accompanied by a change to the Swift 3 migrator that
could automatically convert conditional imports of Darwin/Glibc to the new
We must also devise a strategy for the transient rollout period, when Swift
defines a Libc module, but we don't have an OS X SDK that uses that name in
the bundled module.map. We can add a compiler hack for that, to
transparently translate the name.
# Alternatives considered
I believe there are two contentious points to this proposal:
1. Whether to unify the module name across platforms.
2. What to name the module.
Alternatives considered on point #1 (whether to unify) include:
1a. The status quo: I consider this to be undesirable for the reasons
stated in "Motivation". To reiterate: the current system forces users to go
out of their way to write cross-platform Swift code, as opposed to writing
code that "just works" everywhere.
1b. The current Darwin and Glibc modules are a combination of POSIX and the
C standard library. We could export *two* modules. However I believe this
introduces additional overhead for users, with only the marginal benefit of
clean separation between libc and POSIX.
1c. A special import statement, defined in the Swift stdlib, that would
automatically get preprocessed to the five lines of boilerplate shown
above. This has several downsides, most notably the added complexity to
On point #2 (what to name it), I have spoken with people that raised
concerns over the name "Libc":
Another concern is about compatibility with the C++ modules proposal. If
we want this module name to mean something, it should agree with the C++
I don't know which name was chosen in the C++ spec. I've been searching
WG21 papers with no luck--any advice on how to find out would be
Aside from the above point, some concrete alternatives for point #2 (what
to name it) include:
- `import System`: This is better suited to the idea that the module
contains both POSIX and libc.
- `import C`: Drop the "Lib"--just "C". It's cleaner. (
Thanks for taking the time to read this proposal draft! Feedback (on its
contents or on how to proceed with the evolution proposal process) is
- Brian Gesiak