More Raku Readline fixes
This is a follow-up to the last post about Raku; I recommend reading that if you haven't already.
While I was making the necessary fixes for Raku’s Readline module to install on macOS, I also noticed another special case in the code for OpenBSD. Today I figured I might as well check that out and see if it still works, since OpenBSD has a habit of exposing assumptions and GNU-isms in code that hasn’t been specifically tested on it.
OpenBSD
I installed a fresh copy of OpenBSD 7.4 in a test VM, and ran pkg_add rakudo
to get Rakudo v2022.12. The next step was to use Zef to try and install the
Readline package, but sadly Zef itself is not packaged on OpenBSD. It’s easily
installed from source:
$ git clone https://github.com/ugexe/zef
$ cd zef
$ raku -I. bin/zef install .
Upon trying to run ~/.raku/bin/zef install Readline
I got the following error:
Enabled extracting backends [git path] don't understand /tmp/.zef/[...]e5690da54e9e58e.tar.gz
You may need to configure one of the following backends, or install its underlying software - [tar unzip]
I’ve had enough past experiences with the differences between BSD and GNU tar
to be immediately suspicious that Zef was not either not detecting a tar
command, or was trying to run it in a way that didn’t work on OpenBSD. Since
I’d installed Zef from source it was easy enough to go digging, and I soon
turned up some OpenBSD-specific code in lib/Zef/Service/Shell/tar.rakumod
.
if BEGIN $*VM.osname.lc.contains('openbsd') {
# For OpenBSD run just `tar` and see if the output contains
# any of the following words (which suggest the command exists)
BEGIN my @needles = <archive file specify>;
my $proc = Zef::zrun('tar', :!out, :err);
my $stderr = $proc.err.slurp(:close).lc;
return $probe-cache = any($stderr.words) ~~ any(@needles);
}
Prompted by this code, I ran tar
manually:
$ tar
tar: Failed open to read on /dev/rst0: Permission denied
OpenBSD tar
’s interface must have changed since the detection code was
written; none of the magic words appear in the output, so the command isn’t
detected. After some discussion with ugexe++ on the #raku
IRC channel, we
decided to test by trying to run tar -cf -
instead, since OpenBSD tar
has
no --help
or --version
options.
if BEGIN $*VM.osname.lc.contains('openbsd') {
# On OpenBSD `tar -cf -` should run successfully with no
# output. This would cause a warning with GNU tar.
my $proc = Zef::zrun('tar', '-cf', '-', :!out, :!err);
return $probe-cache = so $proc;
}
With that fixed, let’s try again:
===> Searching for: Readline
===> Extraction: Failed to find a META6.json file for Readline:ver<0.1.7>:auth<zef:clarkema> -- failure is likely
No meta file? Path: /tmp/.zef/1701622764.89702/6bf81b82026641a849aa03175e5690da54e9e58e.tar.gz
Hmm. Well, that file is the same dist tarball that works everywhere else, so
it seems unlikely that it’s missing something as fundamental as its
META6.json
. A quick manual check confirmed that it was indeed present. I
wasted a bit of time thinking that it might be due to differences in tar
handling paths with the -C
option, which has bitten me in the past, but the
problem turned out to be much simpler.
In the ls-files
method, Zef’s tar.rakumod
lists the contents of an archive
like this:
my $proc = Zef::zrun-async('tar', '-t', '-f', $archive-file.basename);
Let’s try running that by hand:
tar -t -f 6bf81b82026641a849aa03175e5690da54e9e58e.tar.gz
tar: input compressed with gzip; use the -z option to decompress it
GNU tar and FreeBSD tar will both run this without complaint, assuming the
missing -z
if the tarball is gzipped — not so OpenBSD tar. Time for the
world’s smallest patch:
- my $proc = Zef::zrun-async('tar', '-t', '-f', $archive-file.basename);
+ my $proc = Zef::zrun-async('tar', '-zt', '-f', $archive-file.basename);
That’s enough to allow Zef itself to work on OpenBSD, so I created a pull request with those two fixes and moved on to Readline.
Readline's OpenBSD code uses the following regular expression to look for candidate libraries:
$library-match = rx/:i libereadline\.so\.(\d+) $/;
It wasn’t finding anything, so I checked the contents of OpenBSD's readline
library (installed via pkg install readline
.) The .so
file we’re
interested in ends up at /usr/local/lib/libereadline.so.3.0
. A quick mental
match of that path against the regex above shows it won’t match, because the
regex only permits .so.X
, not .so.X.Y
, and no symlink is provided from
libereadline.so.3 -> libereadline.so.3.0
Relaxing the regex to allow more complex version numbers is all that’s required for the module to install correctly:
- $library-match = rx/:i libereadline\.so\.(\d+) $/;
+ $library-match = rx/:i libereadline\.so\.(<[ \d . ]>+) $/;
These are all trivial fixes — no more than a few characters — but they’re a good example of the kind of bit-rot that happens naturally over time when dealing with niche languages and operating systems; and their length belies the time required to investigate, patch, and test.
FreeBSD
Since I seem to be on a bit of a roll with different operating systems at the moment I figured I might as well test FreeBSD while I’m at it. I installed the newly-released 14.0 in a VM, and then installed Rakudo. There’s no package for FreeBSD, so this involves building from source, but it was painless.
Happily, the whole process was without issue; Zef and Readline were both fine and within a few minutes I had a REPL up and running with line editing.
Next!
MacPorts
Finally, MacPorts. This isn’t something I use myself, but there was a PR by opoku++ on the repository I forked from to add another path on macOS to support readline from MacPorts.
I rejigged this to work with my updated Homebrew code and committed it. opoku++ was kind enough to test it for me and confirm it works.
Another release
These two changes together to support OpenBSD and MacPorts have become Readline v0.1.8 and are now available via Zef.