bootstrap
Like the fetch
command, bootstrap
resolves and fetches the
JARs of one or more dependencies. Like the launch
command,
it tries to find a main class in those JARs
(unless -M
specifies one already). But unlike launch
, the
bootstrap
command doesn't launch this main class straightaway. Instead, it
generates a (usually tiny) JAR, that can be renamed, copied, moved to another
machine or OS, and is able to download and launch the corresponding application.
For example,
$ coursier bootstrap org.scalameta:scalafmt-cli_2.13:2.3.2 -o scalafmt
$ ./scalafmt --version
scalafmt 2.3.2
Note that boostrap
also accepts the same application names as the
install
command, that are looked for in
channels, so that the following works
$ coursier bootstrap scalafmt -o scalafmt
$ coursier bootstrap scalafmt:2.3.2 -o scalafmt # explicit version
bootstrap content
The generated bootstraps are tiny Java apps, that upon launch, successively
- read a list of URLs for one of their resource files,
- read a main class name from another of their resource files,
- download the URLs missing from the coursier cache,
- get all the files corresponding to these URLs from the coursier cache,
- load those files in a class loader,
- find the main class by reflection,
- call the main method of the main class, which actually runs the application.
Overall, this "bootstraps" the application, hence the name.
All of that logic is handled by a small Java application, which currently
corresponds to the bootstrap-launcher
module
of the coursier sources. A bootstrap consists of that module classes,
along with resource files (for the artifact URLs and main class to load) that
are specific to each application to launch.
Java options
The generated bootstraps automatically pass their arguments starting with
-J
to Java, stripping the leading -J
, e.g.
$ coursier bootstrap com.lihaoyi:ammonite_2.12.8:1.6.0 -M ammonite.Main -o amm
$ ./amm -J-Dfoo=bar
Loading...
Welcome to the Ammonite Repl 1.6.0
(Scala 2.12.8 Java 1.8.0_121)
@ sys.props("foo")
res0: String = "bar"
Alternatively, one can hard-code Java options when generating the bootstrap,
by passing --java-opt
options, like
$ coursier bootstrap com.lihaoyi:ammonite_2.12.8:1.6.0 -M ammonite.Main -o amm \
--java-opt -Dfoo=bar
$ ./amm
Loading...
Welcome to the Ammonite Repl 1.6.0
(Scala 2.12.8 Java 1.8.0_121)
@ sys.props("foo")
res0: String = "bar"
Windows
The launchers generated by the bootstrap
command contain a shell preamble,
and are made executable, so that even though they are JARs, they can be run
straightaway on Linux and OS X. On Windows, the bootstrap
command also
generates a .bat
file along with the launcher (simply appending .bat
to the
launcher file name). This .bat
file allows one to run the launcher on Windows
nonetheless, almost as on Linux / OS X.
To generate this .bat
file on Linux or OS X (to offer to download it for
example), pass --bat
to the bootstrap command, like
$ coursier bootstrap com.geirsson:scalafmt-cli_2.12:1.5.1 -o scalafmt --bat
$ ls -lh scalafmt*
-rwxr-xr-x 1 user group 18K 8 jan 15:24 scalafmt
-rw-r--r-- 1 user group 2,0K 8 jan 15:24 scalafmt.bat
Inversely, to disable generating it on Windows, pass --bat=false
to the
bootstrap
command.
Standalone bootstraps
Rather than having the launcher download its JARs upon first launch, one may
prefer to ship those JARs along with the launcher. The --standalone
option
allows just that. This options adds the application JARs as resources in the
launcher. This inflates the launcher size, but that allows it to run without
relying on the coursier cache or downloading things.
Use like
$ coursier bootstrap com.geirsson:scalafmt-cli_2.12:1.5.1 -o scalafmt --standalone
$ ls -lh scalafmt
-rwxr-xr-x 1 alexandre staff 15M 8 jan 15:31 scalafmt
$ ./scalafmt --version
scalafmt 1.5.1
Standalone bootstraps should be similar to JARs generated by One-JAR. Note that although they ship with all their dependencies like so-called uber JARs or assemblies, they differ from them in the sense that:
- dependencies are still kept separate (each dependency stays in its own JAR, themselves added as JARs to the launcher - these are JARs in JARs), so that no conflict has to be resolved,
- the launchers can't be added as is to a classpath, like standard JARs, or assemblies / uber JARs. This prevents using them as is to package spark jobs in particular.
scala-native
The bootstrap
command of coursier can generate
Scala Native-based native launchers.
This requires the application you want a launcher for to be cross-compiled to
Scala Native, and its Scala Native artifacts to be published to Maven or Ivy
repositories. The echo project of
coursier has
such a module, currently
published as
io.get-coursier:echo_native0.3_2.11:1.0.2
.
In order to generate a launcher for such a published application, you'll need to
have your environment
set up for Scala Native. You can then generate native
launchers by passing the --native
or -S
option to the bootstrap
command,
like
$ coursier bootstrap \
--native \
io.get-coursier:echo_native0.3_2.11:1.0.2 \
-o echo
[info] Linking (2354 ms)
[info] Discovered 1291 classes and 9538 methods
…
and run them, like
$ ./echo hey
hey
Linking flags during the linking phase can be adjusted via LDFLAGS
in the
environment.
GraalVM native image
The bootstrap
command can generate native executables via
GraalVM native image.
For example,
$ coursier bootstrap --native-image \
io.get-coursier:echo:1.0.3 \
-o echo
This command automatically fetches and extracts a GraalVM Community edition archive,
like the java
command of coursier does, then uses it to call
native-image
.
You can pass an explicit GraalVM version via --graalvm-version
or --graalvm
, like
$ coursier bootstrap --native-image \
--graalvm 19.3 \
io.get-coursier:echo:1.0.3 \
-o echo
Note that this option accepts short versions. So as of writing this, 19.3
gets automatically
expanded to 19.3.1
.
You can pass custom options to native-image via --graalvm-option
or
--graalvm-opt
, or after a --
, like
$ coursier bootstrap --native-image \
--graalvm 19.3 \
io.get-coursier:echo:1.0.3 \
-o echo \
--graalvm-opt --no-fallback \
-- \
--enable-all-security-services \
--initialize-at-build-time
Note that it can be more convenient to pass arguments to native-image via a native-image.properties resource rather than on the command-line.
Local artifacts
Some of the JARs of the dependencies passed to the bootstrap
command may
correspond to local files (e.g. from the local ~/.ivy2/local
repository)
rather than files from remote repositories. In that case, these local files
are going to be included as resources, rather than via a URL, in the
generated launchers. That allows these launchers to be copied to other
machines, and work fine even without the original local repositories.
To disable that behavior, pass --embed-files=false
to the bootstrap
command. Note that this only applies when the --standalone
option is
not passed - if it is, JARs are added as resources to the launcher
nevertheless.