Ekam Build System
github.com
github.com
If there is a problem to solve in development it's definitely that one...
I personally have now designed two languages (including three separate iterations of one of them) and three build tools. I say that with shame, not pride.
In my defense, Ekam was at the time I wrote it just a fun weekend project. I wouldn't have done it while on the clock for anything important. :)
And then having written it, it turned out to be useful enough that I kept using it. ::shrug::
The nixpkgs community is then responsible for making sure that the build compiles on all platforms. It's not just for C code either, nix files can be used to declare any kind of dependency. It's a full blown declarative, functional, domain specific programming language for packages. So your project can depend on haskell or python code and everything just works.
There's a heavy linux focus right now but there's a whole group of people making the packages work nicely on osx and cygwin. Check it out: http://nixos.org/nix/
In Ekam's link stage, it starts with a root object (typically, something exporting the symbol `main`) and then tries to satisfy all missing symbols using other built objects, transitively, until none of the remaining missing symbols are satisfied by any known objects. It then hopes those remaining symbols will be covered by libraries specified by the `LIBS` environment variable and so invokes the linker.
Ekam could additionally at this point consult an index of symbols exported by libraries in order to decide which ones to link against. We just need to implement such an index and decide how exactly a library gets included in the index.
I haven't implemented this because with Sandstorm we prefer instead to bring most of the source code for our dependencies into our build tree, so that they are in fact all built with Ekam instead of linking with pre-built system libs. This makes sense for Sandstorm since we intend to run in a chroot environment anyway and so cannot really share libraries with the rest of the system regardless. It's also really nice for development to be able to edit dependencies directly when needed and start using those changes in Sandstorm code without having to install the new library version somewhere as an interim step.
With proper caching it could make local build systems obsolete for many purposes. Maybe it would be easier then to delegate module building in style similar to distcc, but less fragile.
Because I like to walk down the memory lane I've looked at my HN comments regarding modules. Here is one from exactly one year ago [1], 855 days ago [2], 1100 days ago.
[1]: https://news.ycombinator.com/item?id=7491149 [2]: https://news.ycombinator.com/item?id=4836499 [3]: https://news.ycombinator.com/item?id=3613636
Though to be honest, the most valuable feature turned out to be continuous building (in which Ekam will watch (via inotify) the source code for changes, immediately rebuild the affected targets, and communicate errors back to my editor). Instant feedback from my compiler simply makes me more productive. Other build systems could easily implement this too, and a few have, but somehow most build systems still require you to manually invoke a shell command.
Honestly, it would be really great to see some sort of inotify-based continuous building added to gmake (thought don't forget that you also need to get the error log back into your editor for it to be really useful).
(You may be tempted to suggest `while true; do make; done`, but in practice this has all sorts of performance and usability problems.)
- The LD_PRELOAD trick simply rewrites filenames on open() and similar calls, allowing subsequent I/O to happen directly. FUSE unfortunately requires going through the userspace FUSE server for every read or write operation.
- FUSE would require either mounting a new FS on-the-fly for each build step (probably inefficient) or using a pool of FUSEs where each one is reused for multiple commands (requires carefully making sure all state is purged in between).
- Something would have to be done to ensure that no other process on the system (say, a backup or indexing daemon) accidentally wanders into the Ekam FUSE mounts, performing I/O that Ekam interprets as coming from the build step. Mount namespaces could solve this, but assuming you don't want to run Ekam as root, this would require user namespaces, and currently there's no way to mount FUSE devices inside a user namespace.
LD_PRELOAD is not without its own share of problems, though. On Linux, you cannot override intra-library calls, so it's not enough to just override the syscall wrappers, you must also override all libc calls that take paths (e.g. fopen()). Also, some people don't use the libc syscall wrappers, preferring syscall(2) or even raw assembly code (especially true of languages that don't like calling into C), which can't be intercepted by LD_PRELOAD. Oh yes, and statically-linked programs are a problem. (Luckily I haven't run into these problems in anything I actually wanted to run as a build tool.)
If I were rewriting Ekam today, I'd probably use seccomp to raise SIGSYS on actual system calls, and use LD_PRELOAD only to install a SIGSYS handler. This should be cleaner and work for a wider range of programs, though it would be totally Linux-specific. (Currently Ekam only runs on Linux, but its strategy has been shown to work at least on FreeBSD and OSX in the past.)