fsprojects / ProjectScaffold

A prototypical .NET solution (file system layout and tooling), recommended for F# projects
http://fsprojects.github.io/ProjectScaffold
The Unlicense
515 stars 154 forks source link

Make build.sh runnable by (almost) any shell #257

Closed rmunn closed 8 years ago

rmunn commented 8 years ago

In https://gitter.im/ionide/ionide-project, someone was complaining of being unable to run the FAKE build script inside Ionide because his shell was set to Dash, not Bash. And that started me thinking: does the build.sh in Project Scaffold have to use Bash? I know the shebang on the first line specifies Bash, and Bash is considered an essential package (therefore always installed) on Debian-based systems... but will Bash always be installed on every system that the build.sh script might run on?

So I set out to investigate if "bashisms" could be removed from the build.sh script. There were three bashisms: the set -o pipefail line (not available in Dash), the use of the [[ syntax for test (also not available in Dash), and the use of function name() { ... } instead of just name() { ... } for declaring functions. All three of these could safely be removed:

  1. The set -o pipefail option is not available in Dash (actually, Dash appears to lack the whole set -o syntax). There is a way to get the exit code from a command in the middle (or at the beginning) of a pipe in POSIX-compliant shells like Dash, but it's eye-searingly ugly. Thankfully, at no point in build.sh do we rely on pipefail; the only point at which a status code is checked is after running paket.bootstrapper.exe, and that isn't run with a pipe. So set -o pipefail could be safely removed from the script, and we gain Dash compatibility without having to touch the ugly mess that is POSIX-compliant "pipefail-equivalence".
  2. The [[ syntax for test makes writing tests a bit nicer by changing some of the syntax of [ to be a bit friendlier and more predictable. However, the tests currently in build.sh do not make use of any of [['s features, and replacing it with [ makes Dash able to handle the script.
  3. function name() { ... } is clearer and easier to read, but it's essentially just syntactic sugar as it does nothing that name() { ... } doesn't do. Since the latter is what's in the POSIX standard, that's what Dash implements. To make build.sh runnable on Dash, we'll need to do the same.

Also, I found one hidden Bashism when I ran the build.sh script against zsh: the assumption that FSIARGS="--fsiargs -d:MONO" will expand into two parameters when passed without quotation marks into the FAKE command line. It turns out that in zsh, variables are auto-quoted, which is quite nice when you're dealing with filenames that contain spaces... but not what we want in the present case. There is a zsh-specific option to revert to bash's behavior, but that would lose dash compatibility -- and arrays in shell scripts were never well-specified in POSIX, so arrays seemed unwise. Therefore, for the sake of simplicity and since it was only two options, I used two variables so that every shell would behave the same way.

Shells that build.sh now runs in:

Shells that build.sh does NOT run in, and that I won't try to fix: