Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement Jlink plugin #951

Merged
merged 53 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
c12302c
feat: add jlink plugin
vpa1977 Jan 6, 2025
cf7a732
doc: add jlink plugin documentation
vpa1977 Jan 6, 2025
e08828c
doc: amend wordlist to include jlink
vpa1977 Jan 6, 2025
ef11438
doc: include jlink plugin in toc
vpa1977 Jan 7, 2025
555b95b
doc: update wordlist for jlink plugin
vpa1977 Jan 7, 2025
9f87a57
chore: install openjdk-21-jdk for jlink test
vpa1977 Jan 7, 2025
80976e8
doc: add changelog rst
vpa1977 Jan 7, 2025
c629c91
test(jlink): add test for different java version
vpa1977 Jan 7, 2025
52e1075
lint: update formatting
vpa1977 Jan 7, 2025
d447abe
fix: include all jar files in the classpath
vpa1977 Jan 8, 2025
30afbc8
test: add unit tests
vpa1977 Jan 8, 2025
f5d65a4
Merge branch 'main' into jlink-plugin
vpa1977 Jan 8, 2025
44db443
feat: use JAVA_HOME and deduce version from it
vpa1977 Jan 20, 2025
8af0327
doc: update documentation
vpa1977 Jan 20, 2025
a59bfb0
Merge branch 'jlink-plugin' of github.com:vpa1977/craft-parts into jl…
vpa1977 Jan 20, 2025
14ca2d3
Merge remote-tracking branch 'upstream/main' into jlink-plugin
vpa1977 Jan 20, 2025
bc74bd6
lint: dependant -> dependent
vpa1977 Jan 20, 2025
2452b20
test: drop test for java version
vpa1977 Jan 20, 2025
213d18f
doc: add missing words for the spellcheck
vpa1977 Jan 20, 2025
5922bbb
test: drop integration test for java_version property
vpa1977 Jan 20, 2025
6251ec3
lint: drop unused import
vpa1977 Jan 20, 2025
3bb327b
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 23, 2025
b34381b
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 23, 2025
04224b6
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 23, 2025
1a7bda3
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 23, 2025
475b098
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 23, 2025
f3a0c7f
Update docs/reference/changelog.rst
vpa1977 Jan 23, 2025
41ecc03
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 23, 2025
693a1fe
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 23, 2025
0996063
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 23, 2025
a8d6168
Merge remote-tracking branch 'upstream/main' into jlink-plugin
vpa1977 Jan 23, 2025
126ee3d
fix: set and validate JAVA_HOME
vpa1977 Jan 23, 2025
789693c
doc: format yaml blocks
vpa1977 Jan 23, 2025
972b828
doc: rename jar -> JAR
vpa1977 Jan 23, 2025
62b2dcd
doc: openjdk -> OpenJDK
vpa1977 Jan 23, 2025
05625d3
doc: declare openjdk build-package
vpa1977 Jan 23, 2025
fdea496
Merge branch 'main' into jlink-plugin
vpa1977 Jan 23, 2025
c08ba2d
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 24, 2025
52cc3e0
Update craft_parts/plugins/jlink_plugin.py
vpa1977 Jan 24, 2025
318bf6b
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 24, 2025
68c754d
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 24, 2025
4fcb352
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 24, 2025
61e3ac2
Update tests/unit/plugins/test_jlink_plugin.py
vpa1977 Jan 24, 2025
0bf53a5
Update docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 Jan 24, 2025
4935f90
doc: specify how to install openjdk for jlink plugin
vpa1977 Jan 25, 2025
b19af95
doc: fix typo in jlink link
vpa1977 Jan 25, 2025
e472123
Merge branch 'main' into jlink-plugin
vpa1977 Jan 25, 2025
07aaeb0
doc: update changelog
vpa1977 Jan 25, 2025
99d8bf9
fix: add explicitly use jlink options
vpa1977 Jan 29, 2025
b65efb6
doc: exclude jvm from staging
vpa1977 Jan 29, 2025
7647c59
Merge branch 'jlink-plugin' of github.com:vpa1977/craft-parts into jl…
vpa1977 Jan 29, 2025
fb203be
Merge branch 'main' into jlink-plugin
vpa1977 Jan 29, 2025
7a38f56
style: black
mr-cal Jan 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ jobs:
fi
echo "::endgroup::"
echo "::group::JDK"
sudo apt-get install -y openjdk-17-jdk-headless openjdk-21-jdk-headless openjdk-8-jdk-headless
sudo apt-get install -y openjdk-17-jdk openjdk-21-jdk openjdk-8-jdk-headless
vpa1977 marked this conversation as resolved.
Show resolved Hide resolved
echo "::endgroup::"
- name: Install uv
uses: astral-sh/setup-uv@v4
Expand Down
154 changes: 154 additions & 0 deletions craft_parts/plugins/jlink_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
#
# Copyright 2024 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""The JLink plugin."""

from typing import Literal, cast

from overrides import override

from .base import Plugin
from .properties import PluginProperties
from .validator import PluginEnvironmentValidator


class JLinkPluginProperties(PluginProperties, frozen=True):
"""The part properties used by the JLink plugin."""

plugin: Literal["jlink"] = "jlink"
jlink_jars: list[str] = []


class JLinkPluginEnvironmentValidator(PluginEnvironmentValidator):
"""Check the execution environment for the JLink plugin.

:param part_name: The part whose build environment is being validated.
:param env: A string containing the build step environment setup.
"""

@override
def validate_environment(
self, *, part_dependencies: list[str] | None = None
) -> None:
"""Ensure the environment contains dependencies needed by the plugin.

:param part_dependencies: A list of the parts this part depends on.

:raises PluginEnvironmentValidationError: If go is invalid
and there are no parts named go.
"""
self.validate_dependency(
dependency="jlink", plugin_name="jlink", part_dependencies=part_dependencies
)


class JLinkPlugin(Plugin):
"""Create a Java Runtime using JLink."""

properties_class = JLinkPluginProperties
validator_class = JLinkPluginEnvironmentValidator

@override
def get_build_packages(self) -> set[str]:
"""Return a set of required packages to install in the build environment."""
return set()

@override
def get_build_environment(self) -> dict[str, str]:
"""Return a dictionary with the environment to use in the build step."""
return {}

@override
def get_build_snaps(self) -> set[str]:
"""Return a set of required snaps to install in the build environment."""
return set()

@override
def get_build_commands(self) -> list[str]:
"""Return a list of commands to run during the build step."""
options = cast(JLinkPluginProperties, self._options)

commands = []

# Set JAVA_HOME to be used in jlink commands
commands.append(
"""
if [ -z "${JAVA_HOME+x}" ]; then
JAVA_HOME=$(dirname $(dirname $(readlink -f $(which jlink))))
fi
if [ ! -x "${JAVA_HOME}/bin/java" ]; then
echo "Error: JAVA_HOME: '${JAVA_HOME}/bin/java' is not an executable." >&2
exit 1
fi
JLINK=${JAVA_HOME}/bin/jlink
JDEPS=${JAVA_HOME}/bin/jdeps
"""
)

# extract jlink version and use it to define the destination
# and multi-release jar version for the dependency enumeration
commands.append("JLINK_VERSION=$(${JLINK} --version)")
commands.append(
"DEST=usr/lib/jvm/java-${JLINK_VERSION%%.*}-openjdk-${CRAFT_ARCH_BUILD_FOR}"
)
commands.append("MULTI_RELEASE=${JLINK_VERSION%%.*}")

# find application jars - either all jars in the staging area
# or a list specified in jlink_jars option
if len(options.jlink_jars) > 0:
jars = " ".join(["${CRAFT_STAGE}/" + x for x in options.jlink_jars])
commands.append(f"PROCESS_JARS={jars}")
else:
commands.append("PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)")

# create temp folder
commands.append("mkdir -p ${CRAFT_PART_BUILD}/tmp")
# extract jar files into temp folder - spring boot fat jar
# contains dependent jars inside
commands.append(
"(cd ${CRAFT_PART_BUILD}/tmp && for jar in ${PROCESS_JARS}; do jar xvf ${jar}; done;)"
)
# create classpath - add all dependent jars and all staged jars
commands.append("CPATH=.")
commands.append(
"""
find ${CRAFT_PART_BUILD}/tmp -type f -name *.jar | while IFS= read -r file; do
CPATH=$CPATH:${file}
done
find ${CRAFT_STAGE} -type f -name *.jar | while IFS= read -r file; do
CPATH=$CPATH:${file}
done
"""
)
commands.append(
"""if [ "x${PROCESS_JARS}" != "x" ]; then
deps=$(${JDEPS} --class-path=${CPATH} -q --recursive --ignore-missing-deps \
--print-module-deps --multi-release ${MULTI_RELEASE} ${PROCESS_JARS})
else
deps=java.base
fi
"""
)
commands.append("INSTALL_ROOT=${CRAFT_PART_INSTALL}/${DEST}")

commands.append(
"rm -rf ${INSTALL_ROOT} && ${JLINK} --no-header-files --no-man-pages --strip-debug --add-modules ${deps} --output ${INSTALL_ROOT}"
)
# create /usr/bin/java link
commands.append(
"(cd ${CRAFT_PART_INSTALL} && mkdir -p usr/bin && ln -s --relative ${DEST}/bin/java usr/bin/)"
)
return commands
2 changes: 2 additions & 0 deletions craft_parts/plugins/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from .dump_plugin import DumpPlugin
from .go_plugin import GoPlugin
from .go_use_plugin import GoUsePlugin
from .jlink_plugin import JLinkPlugin
from .make_plugin import MakePlugin
from .maven_plugin import MavenPlugin
from .meson_plugin import MesonPlugin
Expand Down Expand Up @@ -57,6 +58,7 @@
"dump": DumpPlugin,
"go": GoPlugin,
"go-use": GoUsePlugin,
"jlink": JLinkPlugin,
"make": MakePlugin,
"maven": MavenPlugin,
"meson": MesonPlugin,
Expand Down
2 changes: 1 addition & 1 deletion craft_parts/sources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@


def _detect_source_type(
data: SourceModel | dict[str, Any]
data: SourceModel | dict[str, Any],
) -> SourceModel | dict[str, Any]:
"""Get the source type for a source if it's not already provided."""
if isinstance(data, BaseSourceModel) or "source-type" in data:
Expand Down
10 changes: 10 additions & 0 deletions docs/common/craft-parts/craft-parts.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ InvalidSourceOption
InvalidSourceOptions
InvalidSourceType
Iterable
JAR
JavaPlugin
JLink
JLinkPlugin
JLinkPluginEnvironmentValidator
JLinkPluginProperties
JSON
LDFLAGS
LLVM
Expand Down Expand Up @@ -145,6 +150,7 @@ NpmPlugin
NpmPluginEnvironmentValidator
NpmPluginProperties
OCI
OpenJDK
OSError
OsRelease
OsReleaseCodenameError
Expand Down Expand Up @@ -313,6 +319,7 @@ chmod
chroot
chrooted
classmethod
classpath
classvar
cli
cls
Expand Down Expand Up @@ -368,7 +375,9 @@ ing
initialized
iterable
iojs
jdeps
jdk
jlink
jre
js
json
Expand Down Expand Up @@ -398,6 +407,7 @@ oci
ok
onboarding
opencontainers
openjdk
organization
organize
organized
Expand Down
107 changes: 107 additions & 0 deletions docs/common/craft-parts/reference/plugins/jlink_plugin.rst
vpa1977 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
.. _craft_parts_jlink_plugin:

JLink plugin
medubelko marked this conversation as resolved.
Show resolved Hide resolved
=============

The `JLink <jlink_>`_ plugin can be used for Java projects where
you would want to deploy a Java runtime specific for your application
or install a minimal Java runtime.


Keywords
--------

This plugin uses the common :ref:`plugin <part-properties-plugin>` keywords as
well as those for :ref:`sources <part-properties-sources>`.

Additionally, this plugin provides the plugin-specific keywords defined in the
following sections.

jlink-jars
~~~~~~~~~~~~~~~~~~
**Type:** list of strings

List of paths to your application's JAR files. If not specified, the plugin
will find all JAR files in the staging area.

Dependencies
------------

The plugin expects OpenJDK to be available on the system and to contain
the ``jlink`` executable. OpenJDK can be defined as a
``build-package`` in the part using ``jlink`` plugin.
Another alternative is to define another part with the name
``jlink-deps``, and declare that the part using the
``jlink`` plugin comes :ref:`after <after>` the ``jlink-deps`` part.

If the system has multiple OpenJDK installations available, one
must be selected by setting the ``JAVA_HOME`` environment variable.

.. code-block:: yaml

parts:
runtime:
plugin: jlink
build-packages:
- openjdk-21-jdk
build-environment:
- JAVA_HOME: /usr/jvm/java-21-openjdk-${CRAFT_ARCH_BUILD_FOR}


The user is expected to stage OpenJDK dependencies either by installing
an appropriate OpenJDK slice:

.. code-block:: yaml

parts:
runtime:
plugin: jlink
build-packages:
- openjdk-21-jdk
after:
- deps

deps:
plugin: nil
stage-packages:
- openjdk-21-jre-headless_security
stage:
- -usr/lib/jvm

Or, by installing the dependencies directly:

.. code-block:: yaml

parts:
runtime:
plugin: jlink
build-packages:
- openjdk-21-jdk
after:
- deps

deps:
plugin: nil
stage-packages:
- libc6_libs
- libgcc-s1_libs
- libstdc++6_libs
- zlib1g_libs
- libnss3_libs


How it works
------------

During the build step, the plugin performs the following actions:

* Finds all JAR files in the staging area or selects jars specified in
``jlink-jars``.
* Unpacks JAR files to the temporary location and concatenates all embedded jars
into `jdeps <jdeps_>`_ classpath.
* Runs `jdeps <jdeps_>`_ to discover Java modules required for the staged jars.
* Runs `jlink <jlink_>`_ to create a runtime image from the build JDK.


.. _`jdeps`: https://docs.oracle.com/en/java/javase/21/docs/specs/man/jdeps.html
.. _`jlink`: https://docs.oracle.com/en/java/javase/21/docs/specs/man/jlink.html
8 changes: 8 additions & 0 deletions docs/reference/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
Changelog
*********

2.5.0 (2025-XX-XX)
------------------

New features:

- Add the :ref:`jlink plugin<craft_parts_jlink_plugin>` for setting up
Java runtime.

2.4.1 (2025-01-24)
------------------

Expand Down
1 change: 1 addition & 0 deletions docs/reference/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ lifecycle.
/common/craft-parts/reference/plugins/dump_plugin.rst
/common/craft-parts/reference/plugins/go_plugin.rst
/common/craft-parts/reference/plugins/go_use_plugin.rst
/common/craft-parts/reference/plugins/jlink_plugin.rst
/common/craft-parts/reference/plugins/make_plugin.rst
/common/craft-parts/reference/plugins/maven_plugin.rst
/common/craft-parts/reference/plugins/meson_plugin.rst
Expand Down
Loading