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:
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".
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.
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:
Bash (still)
Dash
Ksh
Zsh
Shells that build.sh does NOT run in, and that I won't try to fix:
Csh (too old, too different, not worth it)
Fish (too new, too different, and shell-script compatibility doesn't seem to be a goal of the Fish project. If enough people ask, we could write a build.fish script, but it'll be separate from build.sh)
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 thebuild.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: theset -o pipefail
line (not available in Dash), the use of the[[
syntax fortest
(also not available in Dash), and the use offunction name() { ... }
instead of justname() { ... }
for declaring functions. All three of these could safely be removed:set -o pipefail
option is not available in Dash (actually, Dash appears to lack the wholeset -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 inbuild.sh
do we rely on pipefail; the only point at which a status code is checked is after runningpaket.bootstrapper.exe
, and that isn't run with a pipe. Soset -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".[[
syntax fortest
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 inbuild.sh
do not make use of any of[[
's features, and replacing it with[
makes Dash able to handle the script.function name() { ... }
is clearer and easier to read, but it's essentially just syntactic sugar as it does nothing thatname() { ... }
doesn't do. Since the latter is what's in the POSIX standard, that's what Dash implements. To makebuild.sh
runnable on Dash, we'll need to do the same.Also, I found one hidden Bashism when I ran the
build.sh
script againstzsh
: the assumption thatFSIARGS="--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:build.fish
script, but it'll be separate frombuild.sh
)