apposed / jaunch

Launch Programs 𝙔𝙀π™ͺ𝙧 Way! πŸ”
The Unlicense
10 stars 2 forks source link

Jaunch: Launch Programs Your Way!β„’

Jaunch is a native launcher for applications that run inside non-native runtimes, including the Java Virtual Machine (JVM), the Python interpreter, or both.

Quick start

(These instructions are temporary until Jaunch has a stable release.)

  1. Build from source by running make dist.

  2. Copy the dist/launcher/dist\launcher.exe native launcher to your application distribution root, renaming it to match your desired naming. For example, if your application is called Fizzbuzz, you might name it fizzbuzz/fizzbuzz.exe.

  3. Create a directory beneath your application root, into which your application's Jaunch TOML configuration will be stored. This configuration directory can be named jaunch, .jaunch, config/jaunch, or .config/jaunch, depending on your requirements and taste. Or you can customize the allowed configuration directory names by editing the JAUNCH_SEARCH_PATHS list in jaunch.c and matching configDirs list in main.kt.

  4. Copy the TOML files from the dist/jaunch folder to your application distribution's configuration folder, as created in (3).

  5. Rename the launcher.toml file to match your launcher name from (2) (e.g. fizzbuzz.toml).

  6. Edit the renamed TOML file's contents to set the parameters for your application. See the comments in common.toml for detailed guidance. If you don't need the JVM, you can remove the JVM-specific parts. If you don't need Python, you can remove the Python-specific parts.

Building from source

See BUILD.md.

License

To maximize the flexibility of downstream projects to use and adapt this code, Jaunch is Unlicensed. See the UNLICENSE file for details.

Why Jaunch

Do you have a desktop application that runs in the Python interpreter or Java Virtual Machine? You probably want a friendly native launcher, for example a .exe file in the case of Windows, that launches your program, right?

There are many existing ways for both Python and the JVM to achieve this goal; see Alternatives below. But none of them do what Jaunch does:

Jaunch began as a tool to launch Fiji, a rather complex application for the JVM. But Fiji also needed the ability to be launched via Python (i.e. start Python which then starts Java using JPype), to make its in-process Python integration as convenient as possible for users to access. So we figured hey, Jaunch knows how to link to libpython now, so why not support standalone Python apps as well? We do not know of an existing general tool in the Python ecosystem that fills this niche of launching Python without necessarily bundling Python.

Design Goals

Run your program in the same process as the launcher

Discover runtime installations already on the system

Support runtime customization of runtime launch parameters

Customize launcher behavior without recompiling native binaries

Keep as much code in a high-level language as possible

Keep the binary size of the native launcher as small as possible

Architecture

Jaunch consists of two parts:

  1. A native launcher, written in C, kept reasonably minimal.

  2. A "configurator" executable written in Kotlin Native, which does the heavy lifting.

The native launcher (1) will be named after your particular application, it is placed in the base directory of your application. For example, for an application called Fizzbuzz, the launcher could be named fizzbuzz.exe.

The configurator (2) is named jaunch-<os>-<arch>.exe, and placed in the jaunch subdirectory of your application. Examples: for ARM64 Linux it would be named jaunch/jaunch-linux-arm64, whereas for x86-64 Windows it would be named jaunch/jaunch-windows-x64.exe. The reason for the <os>-<arch> suffix is so that portable applications can ship with all needed jaunch configurator executables in the same jaunch folder, without any name clashes.

The native launcher invokes the configurator as a subprocess, passing its entire argv list to the appropriate jaunch program via a pipe to stdin. The jaunch configurator is then responsible for outputting the following things via its stdout:

  1. Number of lines of output.
  2. Directive for the native launcher to perform, or else an error message to display.
  3. A sequence of lines corresponding to the directive:
    • For the JVM:
      1. A path to jvm native library.
      2. TODO: Update this explanation. It's different per directive now.

  4. Number of arguments to the runtime (Python or JVM).
  5. List of arguments to the runtime, one per line.
  6. Main program to run.
    • For Python programs: path to Python script on the file system.
    • For JVM programs: Fully qualified main class name in slash-separated (not dot-separated) format.
  7. Number of arguments to the main program.
  8. List of main arguments, one per line.

To deliver this output, the configurator must do the following things:

If your application's needs along these lines are relatively minimal—e.g. if you bundle a JDK in a known location, and always pass all user arguments as main arguments—you would likely not even need Jaunch's Kotlin/configurator code at all; you could write your own simple jaunch configurator as a shell script and/or batch file.

However, Jaunch was designed to satisfy the needs of applications with more complex command line functionality, which is where the Kotlin configurator comes in. It reads declarative TOML configuration files, which define how Jaunch will make the above decisions. The TOML configuration is its own layer of the architecture, which is best learned by reading the common.toml file directly. With this design, the behavior of Jaunch can be heavily customized without needing to modify source code and rebuild. And for applications that need it, there can be multiple different native launchers in the application base directory that all share the same jaunch configurator native binaries with different TOML configurations.

Alternatives

As so often in technology, there are so many. And yet nothing that does what this program does!

Other Java launching approaches

Executable JAR file

jpackage

You can use jpackage to generate one for every platform you want to support: typically Linux, macOS, and Windows. The jpackage tool is part of the Java Development Kit (JDK), so you might think there is no need for another Java native launcher. But jpackage is inflexible:

All of that said, jpackage is a very nice tool, and if it works for you, use it! But if you want more flexibility—if you want to launch Java Your Way—then try Jaunch.

Call java in its own separate process

E.g., via shell scripts such as Contents/MacOS/JavaApplicationStub.

Lean on command-line tools

E.g. SDKMAN!, cjdk, install-jdk, jgo.

Use a general-purpose Java launcher

install4j by ej Technologies.

launch4j

WinRun4J

Build your own native launcher for Java

JavaCall.jl

hfhbd/jniTest

Example Java launchers

ImageJ Launcher

ijp-imagej-launcher

Why (AstroImageJ)

Other Python launching approaches

PyInstaller

From the PyInstaller website:

PyInstaller bundles a Python application and all its dependencies into a single package. The user can run the packaged app without installing a Python interpreter or any modules.

Briefcase

From the Briefcase website:

Briefcase is a tool for converting a Python project into a standalone native application."

constructor

From the constructor website:

constructor is a tool which allows constructing an installer for a collection of conda packages.