Day 19 – Wrapping Scripts

This is a cross post of https://dev.to/patrickbkr/better-wrapper-scripts-158j

When creating an application in Raku one will at one point typically hit the issue that the application can only be started by calling raku and passing a few arguments. The usual solution is to write a small wrapper shell script on POSIX and a bat script on Windows. But getting these wrapper scripts really right is not trivial. Typical pitfalls are:

  • The script depends on the current working directory
  • It requires Bash and thus can’t work in e.g. BusyBox
  • It fails to process args that contain special chars (e.g. < or > (or many others) combined with bat files is fun)
  • It insta returns to the prompt and then ghost writes to the terminal on Windows

A few years ago I started working on a tool to help generating robust wrappers for this use case. I’ve finally pushed it over the finish line.

I’ve named it the Executable Runner Generator. The name could have been chosen better, but we’ll have to live with it now. It’s written in Raku, but the generated wrappers are independent of the language. Currently it can generate wrappers that work on Windows x86_64 and all sorts of POSIX systems.

In general the task of the wrapper is to put together a command line (and runtime environment like CWD and env vars) and execute the program. How exactly that happens is configured via a set of options. The wrapper has the ability to:

  • Construct paths relative to the wrappers file system location
  • Change path separators on Windows
  • Change the current working directory
  • Specify the command line arguments to pass to the program
  • Forward the arguments passed to the wrapper
  • Add and remove environment variables

How?

Glad you asked! To install run

zef install Devel::ExecRunnerGenerator

Then create a file my-app-runner-config.json with these contents:

program => "<abs>bin/raku",
cwd => "<abs>.",
args => [
    "<abs>share/perl6/site/bin/my-app.raku",
    "<cmd-args>"
],
archs => [
    "posix",
    "windows-x86_64.exe",
],
out-path => ".",
:overwrite

Now run

exec-runner-generator --config=my-app-runner-config.json

Which should leave you with a posix and windows-x86_64.exe file. Congratulations!

For all the non-Raku folks out there, there even is a small webservice that exposes the functionality: https://boekernet.de/erg

Happy adventing everyone!

P.S.
On Windows the generator relies on a native executable written in C to do its magic. There is no exec syscall on Windows. The program works around this by staying alive, letting the child process inherit its file descriptors and once the child finishes, returns with its exit code.

I’m pretty sure this “exec emulation” isn’t perfect. But as I’m not a Windows low level expert I don’t even know what I’m missing. Signals? Security contexts? I don’t know.
So: If you are knowledgeable of the lower Windows APIs, I’d love to get feedback on the implementation or maybe even a patch. You can reach me on many channels.

2 thoughts on “Day 19 – Wrapping Scripts

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.