From 7acb866f109cc04dc3f01e01b7deefd421005459 Mon Sep 17 00:00:00 2001
From: Reid Spencer
+
The System Library was donated to LLVM by Reid Spencer who formulated the - original design as part of the eXtensible Programming System (XPS) which is - based, in part, on LLVM.
+ tool chain and runtime libraries to be more easily ported to new platforms + since (theoretically) only llvm/lib/System needs to be ported. This + library also unclutters the rest of LLVM from #ifdef use and special + cases for specific operating systems. Such uses are replaced with simple calls + to the interfaces provided in llvm/include/llvm/System. Note that + lib/System is not intended to be a complete operating system wrapper (such as + the Adaptive Communications Environment (ACE) or Apache Portable Runtime + (APR)), but only to provide the functionality necessary to support LLVM. +The System Library was written by Reid Spencer who formulated the + design based on similar original work as part of the eXtensible Programming + System (XPS).
@@ -69,43 +78,113 @@The System library's requirements are aimed at shielding LLVM from the variations in operating system interfaces. The following sections define the - requirements needed to fulfill this objective.
+ requirements needed to fulfill this objective. Of necessity, these requirements + must be strictly followed in order to ensure the library's goal is reached.To be written.
+The library must sheild LLVM from all system libraries. To obtain + system level functionality, LLVM must #include "llvm/System/Thing.h" + and nothing else. This means that Thing.h cannot expose any system + header files. This protects LLVM from accidentally using system specific + functionality except through the lib/System interface. Specifically this + means that header files like "unistd.h", "windows.h", "stdio.h", and + "string.h" are verbotten outside the implementation of lib/System. +
+The standard C headers (the ones beginning with "c") are allowed + to be exposed through the lib/System interface. These headers and the things + they declare are considered to be platform agnostic. LLVM source files may + include them or obtain their inclusion through lib/System interfaces.
+The standard C++ headers from the standard C++ library and + standard template library are allowed to be exposed through the lib/System + interface. These headers and the things they declare are considered to be + platform agnostic. LLVM source files may include them or obtain their + inclusion through lib/System interfaces.
To be written.
+Any functions defined by system libraries (i.e. not defined by lib/System) + must not be exposed through the lib/System interface, even if the header file + for that function is not exposed. This prevents inadvertent use of system + specific functionality.
+For example, the stat system call is notorious for having + variations in the data it provides. lib/System must not declare stat + nor allow it to be declared. Instead it should provide its own interface to + discovering information about files and directories. Those interfaces may be + implemented in terms of stat but that is strictly an implementation + detail.
To be written.
+Any data defined by system libraries (i.e. not defined by lib/System) must + not be exposed through the lib/System interface, even if the header file for + that function is not exposed. As with functions, this prevents inadvertent use + of data that might not exist on all platforms.
To be written.
+If an error occurs that lib/System cannot handle, the only action taken by + lib/System is to throw an instance of std:string. The contents of the string + must explain both what happened and the context in which it happened. The + format of the string should be a (possibly empty) list of contexts each + terminated with a : and a space, followed by the error message, optionally + followed by a reason, and optionally followed by a suggestion.
+For example, failure to open a file named "foo" could result in a message + like:
+The "foo:" part is the context. The "Unable to open file" part is the error + message. The "because it doesn't exist." part is the reason. This message has + no suggestion. Where possible, the imlementation of lib/System should use + operating system specific facilities for converting the error code returned by + a system call into an error message. This will help to make the error message + more familiar to users of that type of operating system.
+Note that this requirement precludes the throwing of any other exceptions. + For example, various C++ standard library functions can cause exceptions to be + thrown (e.g. out of memory situation). In all cases, if there is a possibility + that non-string exceptions could be thrown, the lib/System library must ensure + that the exceptions are translated to std::string form.
To be written.
+None of the lib/System interface functions may be declared with C++ + throw() specifications on them. This requirement makes sure that the + compler does not insert addtional exception handling code into the interface + functions. This is a performance consideration: lib/System functions are at + the bottom of the many call chains and as such can be frequently called. We + need them to be as efficient as possible.
To be written.
+The implementation of a function for a given platform must be written + exactly once. This implies that it must be possible to apply a function's + implementation to multiple operating systems if those operating systems can + share the same implementation.
There must be no functionality specified in the interface of lib/System + that isn't actually used by LLVM. We're not writing a general purpose + operating system wrapper here, just enough to satisfy LLVM's needs. And, LLVM + doesn't need much. This design goal aims to keep the lib/System interface + small and understandable which should foster its actual use and adoption.
+The entry points specified in the interface of lib/System must be aimed at + completing some reasonably high level task needed by LLVM. We do not want to + simply wrap each operating system call. It would be preferable to wrap several + operating system calls that are always used in conjunction with one another by + LLVM.
+For example, consider what is needed to execute a program, wait for it to + complete, and return its result code. On Unix, this involves the following + operating system calls: getenv, fork, execve, and wait. The + correct thing for lib/System to provide is a function, say + ExecuteProgramAndWait, that implements the functionality completely. + what we don't want is wrappers for the operating system calls involved.
+There must not be a one-to-one relationship between operating + system calls and the System library's interface. Any such interface function + will be suspicious.
+Operating system interfaces will generally provide errors results for every + little thing that could go wrong. In almost all cases, you can divide these + error results into two groups: normal/good/soft and abnormal/bad/hard. That + is, some of the errors are simply information like "file not found", + "insufficient privileges", etc. while other errors are much harder like + "out of space", "bad disk sector", or "system call interrupted". Well call the + first group "soft" errors and the second group "hard" errors.
+
lib/System must always attempt to minimize soft errors and always just + throw a std::string on hard errors. This is a design requirement because the + minimization of soft errors can affect the granularity and the nature of the + interface. In general, if you find that you're wanting to throw soft errors, + you must review the granularity of the interface because it is likely you're + trying to implement something that is too low level. The rule of thumb is to + provide interface functions that "can't" fail, except when faced with hard + errors.
+For a trivial example, suppose we wanted to add an "OpenFileForWriting" + function. For many operating systems, if the file doesn't exist, attempting + to open the file will produce an error. However, lib/System should not + simply throw that error if it occurs because its a soft error. The problem + is that the interface function, OpenFileForWriting is too low level. It should + be OpenOrCreateFileForWriting. In the case of the soft "doesn't exist" error, + this function would just create it and then open it for writing.
+This design principle needs to be maintained in lib/System because it + avoids the propagation of soft error handling throughout the rest of LLVM. + Hard errors will generally just cause a termination for an LLVM tool so don't + be bashful about throwing them.
+Rules of thumb:
++Notes: +10. The implementation of a lib/System interface can vary drastically between + platforms. That's okay as long as the end result of the interface function is + the same. For example, a function to create a directory is pretty straight + forward on all operating system. System V IPC on the other hand isn't even + supported on all platforms. Instead of "supporting" System V IPC, lib/System + should provide an interface to the basic concept of inter-process + communications. The implementations might use System V IPC if that was + available or named pipes, or whatever gets the job done effectively for a + given operating system. + +11. Implementations are separated first by the general class of operating system + as provided by the configure script's $build variable. This variable is used + to create a link from $BUILD_OBJ_ROOT/lib/System/platform to a directory in + $BUILD_SRC_ROOT/lib/System directory with the same name as the $build + variable. This provides a retargetable include mechanism. By using the link's + name (platform) we can actually include the operating specific + implementation. For example, support $build is "Darwin" for MacOS X. If we + place: + #include "platform/File.cpp" + into a a file in lib/System, it will actually include + lib/System/Darwin/File.cpp. What this does is quickly differentiate the basic + class of operating system that will provide the implementation. + +12. Implementation files in lib/System need may only do two things: (1) define + functions and data that is *TRULY* generic (completely platform agnostic) and + (2) #include the platform specific implementation with: + + #include "platform/Impl.cpp" + + where Impl is the name of the implementation files. + +13. Platform specific implementation files (platform/Impl.cpp) may only #include + other Impl.cpp files found in directories under lib/System. The order of + inclusion is very important (from most generic to most specific) so that we + don't inadvertently place an implementation in the wrong place. For example, + consider a fictitious implementation file named DoIt.cpp. Here's how the + #includes should work for a Linux platform + + lib/System/DoIt.cpp + #include "platform/DoIt.cpp" // platform specific impl. of Doit + DoIt + + lib/System/Linux/DoIt.cpp // impl that works on all Linux + #include "../Unix/DoIt.cpp" // generic Unix impl. of DoIt + #include "../Unix/SUS/DoIt.cpp // SUS specific impl. of DoIt + #include "../Unix/SUS/v3/DoIt.cpp // SUSv3 specific impl. of DoIt + + Note that the #includes in lib/System/Linux/DoIt.cpp are all optional but + should be used where the implementation of some functionality can be shared + across some set of Unix variants. We don't want to duplicate code across + variants if their implementation could be shared. ++
To be written.
To be written.
-