On Wed, Feb 21, 2018 at 11:03:13AM +0000, Russel Winder wrote: [...] > I have been (still am?) a SCons fan but it has some serious problems > in some workflows. > > I think all of the things you mention are solved in a system that has > a general purpose programming language to describe the project and the > build. There is clearly a tension between specifying a project in a > purely declarative way (cf. Cargo, Dub, Maven) where all build and > deploy activities are effectively hardwired (though Maven has plugins > to amend things) versus systems that use a programming language. [...]
I think the ideal situation straddles the divide between declarative build specs and a full-fledged general programming language. You don't want it to get too general, lest you end up with the build equivalent of spaghetti code where the build script becomes unreadable and unmaintainable. OTOH a purely declarative approach is limited by how well the DSL is designed. An insufficiently-expressive declarative language leads to much frustration when you find yourself unable to express something that you need to do with your build. Ultimately, at the bottom level, we're just dealing with DAGs, so at some level the build spec should simply be a bunch of node specs, where each node consists of some set of inputs, some set of outputs, and a computational black box (the build action) to get from the former to the latter. This black box can be anything at all, and should not be unnecessarily constrained. On a higher level, though, specifying individual nodes may not always be desirable (e.g., you want to be able to express things like "all .d files in this directory", or "all recursive import dependencies of source file F"). Typically you'd want a slightly higher-level language for generating DAG node specs. Here is where some serious thought needs to be put into designing a language that's both straightforward for common tasks, yet expressive enough to handle unusual tasks. To abuse a metaphor, DAG node specs are the assembly language of build systems, and generally you want to "program" your builds in a higher-level language, but you could either end up with the build equivalent of C with the build analogues of unchecked array bounds, uncontrolled pointer arithmetic, and hundreds of gotchas completely non-obvious to the uninitiated, resulting in ugly, unmaintainable build scripts, or the build equivalent of D with its expressive power yet packaged in a clean syntax, resulting in clean build scripts that are comfortable to write and maintain. So far I've not yet decided which approach is better: using a full-fledged programming language as the high-level API, like SCons does, or a more constrained but more easily controlled (and implemented!) declarative DSL that is not necessarily Turing-complete. Either way, I prefer baking in as little special behaviour as possible, in the sense that any special capabilities like build-in source code dependency scanning or linker flag control ought to be built on top of primitives that are exposed to user build scripts, so that at least in theory, user build scripts could also attain to equivalent capability without needing to hack the build tool. Generally, I dislike any disparity between what built-in constructs can do "magically" vs. what user code (build scripts) can achieve, because that usually means that things will work well as long as you remain within the parameters the author has conceived, but should you ever encounter a situation the author didn't anticipate, you're stuck up the creek without a paddle. T -- Philosophy: how to make a career out of daydreaming.