Create standalone Racket binaries for multiple target platforms
Install package `libssl-dev`
Use Racket 8.5 for build
Fix license and update version



You can also use your local clone with git send-email.



raco-exe-multitarget is a wrapper around raco-cross to easily create Racket standalone binaries for multiple target platforms in one invocation.

This tool is supposed to be run from the directory containing the top-level info.rkt file for your package.


todo-txt [main]$ raco exe-multitarget \
        --targets x86_64-linux,x86_64-win32 \
        --launcher-name-template "{launcher-name}-{version}-{target}" \
        --version 1.2.3 \
[Lots of output from different raco commands ...]

todo-txt [main]$ ls -1 build

For each target, the execution roughly corresponds to:

  • raco cross --target target pkg remove <package-name>
  • raco cross --target target pkg install --deps search-auto
  • raco cross --target target exe --orig-exe --embed-dlls <launcher-path>

The first two steps are used to install all necessary dependencies in each target's raco-cross directory.

After executing these three steps for each target, raco exe-multitarget removes, starting from the current directory, all .dep and .zo files in compiled directories. This is necessary since the package is installed as a linked package, so raco cross generates .dep and .zo files -- possibly for a non-native target -- under the current directory. When using these files in the native environment (outside raco cross), this results in a fasl-read exception. The removal of the .dep and .zo files avoids this error.

Note that the .dep und .zo files are only removed under the current directory. If other dependencies had been installed as linked packages, their directories may still contain .dep and .zo files for non-native targets, leading to the fasl-read error. A future enhancement of raco-exe-multitarget may optionally remove .dep and .zo files in such other linked directories.

Tip: raco cross, even for a single target, takes a while to install even a minimal installation for this target. Therefore, if you haven't used raco cross before and want to experiment with raco exe-multitarget, run it first with only one target to get an idea of the needed time. Fortunately, after the initial setup for a target, the compilation for this target will be much faster.

#Command line

The full command line syntax is described in the --help output:

usage: raco exe-multitarget [ <option> ... ] <launcher-path> [<launcher-paths>] ...

  `raco exe-multitarget` creates standalone binaries for different platforms.

<option> is one of

  -t <targets>, --targets <targets>
     comma-separated list of targets (default: native target)
  -b <build-directory-template>, --build-directory-template <build-directory-template>
     build directory template (default: "build"). See also the information on
     template variables below.
  -l <launcher-name-template>, --launcher-name-template <launcher-name-template>
     launcher name template (default: "{launcher-name}-{version}-{target}" if
     `--version` is specified, otherwise "{launcher-name}-target"). See also
     the information on template variables below.
  -V <version>, --version <version>
     version string
  -n <package-name>, --package-name <package-name>
     package name (default: base name of current directory)
     create binaries from `gracket` instead of `racket`
  --help, -h
     Show this help
     Do not treat any remaining argument as a switch (at this level)

 Multiple single-letter switches can be combined after
 one `-`. For example, `-h-` is the same as `-h --`.

 The options `--build-directory-template` and `--launcher-name-template`
 can contain the following variables in curly brackets:

   {launcher-name}  This variable is derived from each launcher path on
     the command line. The value is the base name minus the ".rkt" suffix.
     For example, for a launcher path `/path/to/my-launcher.rkt`,
     `{launcher-name}` is "my-launcher".
   {target}  `raco cross` target as specified with the `--targets` option.
     Valid values are shown by `raco cross --browse`, for example
     `x86_64-linux`. Currently only targets for the cs VM are allowed.
   {nice-target}  The same as `target`, but the suffix "win32" is changed
     to "windows". This is intended to avoid confusion for end users from
     the seeming contradiction of the name parts "x86_64" (64 bit) and
     "win32" (32 bit).
   {version}  This template variable contains the version string as specified
     with the `--version` option. Note: If no `--version` option is used,
     `{version}` is invalid!

   raco exe-multitarget \
     --targets x86_64-linux,x86_64-win32 \
     --build-directory-template "build-{target}" \
     --launcher-name-template "{launcher-name}-{version}-{nice-target}" \
     --version 1.2.3 \
   will generate the executable files
   Note that the build directory template uses "{target}" and the launcher
   name template uses "{nice-target}".

launcher-paths are the paths of the Racket files that should be compiled to target executables.

Valid target names are those listed by raco cross --browse.

Racket package

Source code

Issue tracker

#Open issues

  • Currently the option --vm for raco cross is hardcoded as "cs". Do we need the bc VM? Add it later if necessary.

  • Version AUTO to automatically determine version from info.rkt or by other means? However, this may not work reliably, so maybe it's better not to support it.

  • When running raco exe-multitarget with a non-native target as the last one, the .dep and .zo files left by raco cross are ones for the non-native platform and a direct invocation (i.e. outside of raco cross) of the launcher fails. Example:

      $ racket file/todoreport.rkt --help
      fasl-read: incompatible fasl-object machine-type 'ta6nt found in #<binary input port bytevector>
         body of data

    Possible ways to deal with this:

    - Remove `.dep` and `.zo` files after running `raco exe-multitarget`. This
      has the disadvantage that a launcher will run more slowly since it can't
      use the compiled files anymore if the user had created them intentionally.
      This is the currently implemented approach.
    - Run `raco make <launcher-path>` for each launcher outside `raco cross`.
      However, if no `.dep` and `.zo` files existed before, running `raco make`
      kind of "freezes" certain settings. For example, if `errortrace` is
      added after the dep/zo compilation, running the launcher will use old
      compiled files and the change from/to `errortrace` doesn't have an effect.
      This can be frustrating to debug.

    For all these approaches, it's important to keep in mind that if the user had run raco make and then added more code, the compiled files may be incomplete and it would be futile if we tried to keep track of which compiled files existed before running raco exe-multitarget, with the thought of creating the same compiled files as existed previously. This is too brittle. As a workaround, maybe add an option --make (or different name) to run raco make <launcher-path> after all cross-compilations.

  • Relatedly, if the current project directory references linked packages as dependencies, the dep and zo files there will also implicitly be overwritten by raco cross with possibly non-native binary files. Currently, raco-exe-multitarget doesn't deal with this problem.

  • For some scenarios raco dist instead of raco exe might be needed. Maybe even combine the raco dist result with AppImage creation under Linux?

  • I'm open to changing the name of the package. For example, if the tool can eventually use raco dist in addition to raco exe, the "exe" in the name may be misleading.

  • More, depending on feedback.