While thinking about my previous post, hermetic build systems, I realized that while a file-generating rule can be the basis, the most important rules are the binary rules, so a 'genrule' must have the option to output binaries.
Also, the binary rules must have the ability to get all the transitive dependencies it has. Imagine you're building an executable from any language, if any of the code is shared then you don't want to mention that shared code's dependencies every time you use it. Instead you want to specify the dependencies once along with the shared code, then have other binaries depend on that specification being up to date. Therefore, you'll be building binaries by using all of its transitive dependencies, since anything using that shared code likely won't depend directly on its dependencies.
What this means is that there has to be a way for a build rule to know about all of its transitive dependencies in order to include their outputs and information in its own result.
So far, I've been language independent in this, but here's an example where c++ needs this. A c++ shared library will likely output a .o file, while a binary will need to link 1 or more .o files together with any libraries they all need. If one of the shared libraries needed, say, libm, then how would the binary generator know other than by iterating through all dependencies and looking for any libraries they need, then adding the necessary -l options to its command line.
This would also happen with shared code that depends on other shared code, and would affect uncompiled/unlinked targets like Python and bash binaries.
Therefore, for a genrule to be the basis of a build system and to be able to have multiple layers of dependencies, the genrule must have access to all its dependencies. Using something like shell may have worked for Makefiles of old, but something with a little more power is necessary to do all this. Therefore, I'm looking for a way to use a function with any globals it uses as the genrule's 'execute'. One of the specs of my design is that a build server and a build client can be separated by a network, allowing a client to use multiple servers to speed up and parallelize each build, so one of the drawbacks of using a more powerful language means serializing a function isn't as simple anymore.