<div dir="ltr">Hi everyone, <br><br>I&#39;ve been working through some issues around figuring out the proper way of handling portable Swift code, specifically around libc, as part of adding support for Fuchsia to swift.<br><br>Some of this has been discussed on this list in the past but I wanted to start a new thread to discuss some ideas on ways to fix some of it.<br><br>The issues I&#39;ve noticed in the setup today:<br><br> *  The Glibc module name is not ideal for Windows, BSD, Fuchsia, Android, PS4, etc where it&#39;s being used today. It&#39;s also not ideal on Linux where people use musl (like arch-linux), uClibc (on embedded systems), or dietlibc. <br><br> *  `Darwin` vs `Glibc` modules provide the same (generally speaking) APIs but force you to do a preprocessor check on which to include.<br><br> *  There is a very common `#if os(Linux)` check people do when importing Darwin vs Glibc that is not inclusive of other OSes when that the code would likely work other platforms. (Like swiftpm does: <a href="https://github.com/apple/swift-package-manager/blob/master/Sources/libc/libc.swift">https://github.com/apple/swift-package-manager/blob/master/Sources/libc/libc.swift</a>) This is fixed partially thanks to canImport but you still have to manually import one of two (or possible more) libc modules depending on this check.<br><br> *  Commonly used libc functions in glibc and musl are inaccessible in Swift because they hidden behind feature test macros that we don&#39;t define when generating the Glibc module. See <a href="https://linux.die.net/man/7/feature_test_macros">https://linux.die.net/man/7/feature_test_macros</a> <br><br> *  Different libcs may expose the same types and functions in different headers. This generally isn&#39;t an issue if you are including just the root module for Glibc but can be brittle if you are including something like Glibc.C.stdlib to get a type that might be defined in Glibc.C.string instead on another system for example.<br><br> *  We currently have no good way to test to see if the libc you are building against actually contains the method you want to call at compile time easily. This might be partially solved with canImport() but can be brittle for the previous issue as well. <br><br> *  Because of limitations in the clang importer, glibc.modulemap.gyp bakes in absolute paths on the build machine which means you can&#39;t use relocatable sysroots (which means a toolchain for Android or Fuchsia can&#39;t be packaged and distributed easily). <br><br>We could definitely try to spend some time to try and cook up the perfect solution and then come up with a plan to get there incrementally, or we could come up with something that is a bit more pragmatic and smooths out most of the problems we have today that leaves room for something better down the road. <br><br>I&#39;m a fan of the latter solution myself so in that spirt I have a high level proposals to solve some of this. <br><div style="font-size:12.8px"><br></div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px"><b>- Create a new &quot;PlatformLibC&quot; module - </b></div><div style="font-size:12.8px"><br></div>Swift should provide a common &quot;PlatformLibC&quot; module as part of the stdlib to use instead of Darwin or Glibc. <br><br>Internally this module will just re-export both the Darwin, Glibc, and any other libc module maps that are created (like for Windows or Fuchsia) under a single module namespace.<br><br>Another idea is having an umbrella header of sorts, referenced in a module map for this PlatformLibC module, that just includes headers from the platform manually (potentially with feature defines like _GNU_SOURCE on Linux). I&#39;m not sure if this solution will work side by side with the Glibc and Darwin modules however since the clang importer I&#39;ve noticed gets angry when two modules specify imports for the same header path. (Someone that knows the clang importer better can probably explain this better)<br><br>After it&#39;s implemented we encourage and drive devs to include this module over the Darwin or Glibc modules directly so we can move away from platform checks for Libc access. <br><br>This module would be unprincipled and would be allowed to expose more symbols and functions than may be strictly necessary and devs could use `canImport` on it for some very basic compile time feature testing.   <div style="font-size:12.8px"><br></div><div style="font-size:12.8px"><b>- Defining _GNU_SOURCE for the Glibc module -</b><br></div><div style="font-size:12.8px"><br></div>Most of the more interesting libc types and functions implemented since c99 and later POSIX standards are hidden behind feature macros in glibc. A number of people have hacked around this issue including in swiftpm and externally (like this crazy hack <a href="https://github.com/dunkelstern/UnchainedGlibc">https://github.com/dunkelstern/UnchainedGlibc</a>) <br><br>This was something that was discussed on this list in 2016 but nothing came of it. <br><br>I went ahead and implemented this with in <a href="https://github.com/apple/swift/pull/13105">https://github.com/apple/swift/pull/13105</a> but later changed it to be Fuchsia specific when we learned doing this may cause some source breakage for Linux users.<br><br>After investigating, the only source breaking change this causes is specifically with the strerror_r() function which changes it&#39;s signature when _GNU_SOURCE is defined (it returns a char* instead of an int). I&#39;m not sure how wide spread this function is used in Swift code directly today (I know that swiftpm uses it which is what broke when we tried this change on Linux).<br><br>If this change is likely to break too many people, one partial fix is that we could reduce the feature define we set when compiling the glibc module down to something that exposes more libc functions but doesn&#39;t break this one specific function&#39;s signature in Glibc. Something like _XOPEN_SOURCE or _BSD_SOURCE would work. We still miss out on a few dozen useful GNU specific libc functions like dladdr, dup3, and pipe2 so it doesn&#39;t really solve the problem fully.<br><br>Alternatively (and I&#39;m not even sure this will work because it&#39;s pretty crazy) is we could remap the strerror_r function, as the clang importer sees it, to an obfuscated name with an APInote and then implement a portable wrapper in C that just calls the posix defined version of this function and we expose that in the Glibc module manually with the current method signature today (like how we handle sem_open).<div style="font-size:12.8px"><br></div><div style="font-size:12.8px"><b><br></b></div><div style="font-size:12.8px"><b>- JIT the Glibc (and/or PlatformLibC) module on the compiler host against the target&#39;s</b><b> sysroots headers instead of building it as part of the stdlib build AOT - </b></div><div style="font-size:12.8px"><br></div>This is a bit more complex of an idea to implement but this would more or less make the Glibc module as flexible as the sdk/platform provided with the Darwin module today. <br><br>Instead of compiling the Glibc module (and some of the stdlib/platform folder) with the toolchain, we move it out so that it&#39;s compiled and cached on the target machine and rebuilt for each target -sdk passed in to swiftc. That way the glibc module will always match the current targeted libc headers you are targeting and will not be based on whatever the build machine for the toolchain&#39;s libc headers looked like at the time. <br><br>For example, if I get a prebuilt swift toolchain for Linux from somewhere and point it at an old RedHat sysroot with maybe a different and possibly customized libc, the glibc module that is compiled and cached for me right then will match that targets libc. <br><br><br>Do these proposals sound reasonable? The first and third idea I could see needing an formal evolution proposal but I&#39;m more about the second one. <br><br>Thanks!<br><br>- Zac Bowling<br><a href="mailto:zbowling@google.com">zbowling@google.com</a></div>