Main opts with space broken apart
Description
Environment
macOS
clj version 1.9.0.358
Attachments
Activity
Alex Miller February 17, 2021 at 10:40 PM
I have implemented a fix for this in Clojure prerelease 1.10.2.786. I changed the format of the file written by the Clojure code to be arg per line (not single space-separated line), and then used bash read to read that file into an array with IFS set to \n - this does the "right" thing and will not word-split on spaces. In Windows, did similar approach with Get-Content reading into array.
Note, the IFS/read solution is available in bash 3.2 which has been the default install for Macs since forever. bash 4.0+ is GPL and not installed by default on Macs (but you can upgrade with brew etc). In bash 4, I believe the preferred solution would be to use mapfile, but I've explicitly NOT chosen to use this due to the Mac version issues, noting here for the historical record.
New Clojure versions automatically invalidate all existing caches (due to staleness check on the installed root deps.edn) so there should be no issue with having new invocations see old cached files in different format. I tested this on both Mac and Windows.
dottedmag@dottedmag.net December 6, 2018 at 10:34 PM
I have attached brew-install-nul-terminated.patch
and tda-nul-terminated.patch
that change format of .main
file to have args NUL-terminated rather than space-separated.
Backward compatibility:
the patches changes cache ids, so caches with old
.main
format won't be reusedNUL characters in
:main-opts
previously were treated inconsistently: under Bash < 4.3 (e.g. MacOS) the remainder of the argument after NUL character was skipped, under Bash >= 4.3 (e.g. fresh Linux) NUL character was skipped, so we're not breaking anything here by reusing NUL as an argument delimiter.
OS compatibility:
Bash >= 2.04 (released ca. 2000, all relevant OSes have newer Bash)
Alternative approaches:
Escaping spaces instead of NUL-terminating lines
Upsides: none?
Downsides: Bash parsing code becomes much more convoluted (unless one resorts to
eval
with its associated issues)
Using
.main2
files instead of changing hashes for existing cache entriesUpsides: existing cache entries that don't need a
.main
file can be reused after upgradeDownsides: additional convoluted logic in
clojure
is needed to detect cache entries with.main
file, treat them as stale, and remove old.main
files after refreshing the cache.
Paulus Esterhazy December 4, 2018 at 10:27 PM
As an alternative to `eval`, the Bash code can read the string into an array manually, character by character. Arguments need to be separated by a delimiter.
Three possible delimiters come to mind:
NUL: won't work because `readarray` is not available in Bash3 (mandated by macOS) and NUL can't be stored in Bash variables
space
newline
As newlines are less likely to appear in main opts, that would be my choice. However, newlines might still occur so they should be escaped by the Clojure code using a backslash. In addition, literal backslashes need to be escaped as well.
I've done a proof of concept that this works with Bash3: https://github.com/clojure/brew-install/pull/3/files#diff-b3212e45e19f61de4754a755466b793f
If that sounds right, I'll update the patch.
Andrea Richiardi December 2, 2018 at 11:35 PM
Very useful scripts indeed (I mean try and demo)
Paulus Esterhazy December 2, 2018 at 8:24 PM
Another option is to check the input before eval using
Details
Details
Assignee
Reporter
Priority

src/foo/core.clj
Expectation is that there be two options passed to
foo.core/-main
, not three.