[[using-shell-building]]
= Building

This section covers how to build a Spring Shell application.

== Starters

. Spring Shell Starters
[]
|===
|Name |Description

|spring-shell-starter| Basic Spring Shell modules
|spring-shell-starter-jansi| With JLine jansi provider
|spring-shell-starter-jni| With JLine jni provider
|spring-shell-starter-jna| With JLine jna provider
|spring-shell-starter-ffm| With JLine ffm provider (requires JDK22+)
|spring-shell-starter-test| Spring Shell testing support
|===

== Terminal Providers

Interacting with an underlying terminal where your program is running has
traditionally been relatively complex process while it may look like
there's not that much happening as it's all just text.

Remember all those old manual typewriters or matrix printers?
A character is printed then a cursor needs to be moved
if printing in a different position. In a nutshell that's how current
terminal emulators work.

To access and understand existing terminal emulators environment better,
JLine can use native code via its own shared libraries. JLine detects
which providers are present and then makes a choice which one to use.
Traditionally there's been 3 providers, `jansi`, `jni` and `jna` which
should all provide same functionalities.

Our starters can be used to specifically pick some of these JLine
providers.

== FFM

With `JDK22` a _Foreign Function and Memory API_ came out from a preview
which is supposed to be a replacement for `JNI` providing much better
and safer native API.

Starting from `3.4.x` we've added a support to compile Spring Shell
application with `JLine` `ffm` terminal provider. This obviously mean
that application needs to be run with `JDK22+`. There is a new JDK
intermediate release every 6 months and long term support(LTS) release
every 2 years. Until there's an existing LTS release Spring Shell can
align with Spring Framework we will use latest JDK release. Obviously
this means that you may need to upgrade your JDK in an inconvenient
time if you choose to use `ffm`. We're also bound to JDK version
`JLine` itself uses to compile its `ffm` parts.

FFM itself will cause jvm to print warnings when some part of it are
used. These warnings are obviously annoying with terminal applications
as it may interfere and cause a little bit of a mess. In future JDK
versions these warnings will also be added for an older JNI modules and
at some point these warnings will be changed into hard errors. User will
be required to enable these native "unsafe" parts manually.

JVM option for this in a command line is:

[source, bash]
----
--enable-native-access=ALL-UNNAMED
----

If you have a jar file you can have this setting in its `META-INF/MANIFEST.MF`.

[source]
----
Enable-Native-Access: ALL-UNNAMED
----

Which can be added during a build i.e. if using gradle:

[source, groovy]
----
tasks.named("bootJar") {
    manifest {
        attributes 'Enable-Native-Access': 'ALL-UNNAMED'
    }
}
----

IMPORTANT: What comes for enabling native parts in a JDK, JLine has been
proactive and already has a check for this and will throw error if
native access is not enabled.

[[native]]
== Native Support

Support for compiling _Spring Shell_ application into a _GraalVM_ binary
mostly comes from _Spring Framework_ and _Spring Boot_ where feature is
called _AOT_. Ahead of Time means that application context is prepared
during the compilation time to being ready for _GraalVM_ generation.

Building atop of _AOT_ features from a framework _Spring Shell_ has its
own _GraalVM_ configuration providing hints what should exist in
a binary. Usually trouble comes from a 3rd party libraries which doesn't
yet contain _GraalVM_ related configurations or those configurations
are incomplete.

IMPORTANT: It is requred to use _GraalVM Reachability Metadata Repository_ which
provides some missing hints for 3rd party libraries. Also you need to have
_GraalVM_ installed and `JAVA_HOME` pointing to that.

For _gradle_ add graalvm's native plugin and configure metadata repository.

[source, groovy, subs=attributes+]
----
plugins {
	id 'org.graalvm.buildtools.native' version '0.9.16'
}

graalvmNative {
	metadataRepository {
        enabled = true
	}
}
----

When gradle build is run with `./gradlew nativeCompile` you should get binary
under `build/native/nativeCompile` directory.

For `maven` use `spring-boot-starter-parent` as parent and you'll get `native`
profile which can be used to do a native compilation. You need to configure metadata repository:

[source, xml, subs=attributes+]
----
<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
                <configuration>
                    <metadataRepository>
                        <enabled>true</enabled>
                    </metadataRepository>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
----

NOTE: If you rely on `spring-boot-starter-parent` it manages `native-maven-plugin`
version which is kept up to date.

When maven build is run with `./mvnw native:compile -Pnative` you should get binary
under `target` directory.

If everything went well this binary can be run as is instead of executing
boot application jar via jvm.
