# -*- 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."""fromtypingimportLiteral,castfromoverridesimportoverridefrom.baseimportPluginfrom.propertiesimportPluginPropertiesfrom.validatorimportPluginEnvironmentValidator
[docs]classJLinkPluginProperties(PluginProperties,frozen=True):"""The part properties used by the JLink plugin."""plugin:Literal["jlink"]="jlink"jlink_jars:list[str]=[]
[docs]classJLinkPluginEnvironmentValidator(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. """
[docs]@overridedefvalidate_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)
[docs]classJLinkPlugin(Plugin):"""Create a Java Runtime using JLink."""properties_class=JLinkPluginPropertiesvalidator_class=JLinkPluginEnvironmentValidator
[docs]@overridedefget_build_packages(self)->set[str]:"""Return a set of required packages to install in the build environment."""returnset()
[docs]@overridedefget_build_environment(self)->dict[str,str]:"""Return a dictionary with the environment to use in the build step."""return{}
[docs]@overridedefget_build_snaps(self)->set[str]:"""Return a set of required snaps to install in the build environment."""returnset()
[docs]@overridedefget_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 commandscommands.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 enumerationcommands.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 optioniflen(options.jlink_jars)>0:jars=" ".join(["${CRAFT_STAGE}/"+xforxinoptions.jlink_jars])commands.append(f"PROCESS_JARS={jars}")else:commands.append("PROCESS_JARS=$(find ${CRAFT_STAGE} -type f -name *.jar)")# create temp foldercommands.append("mkdir -p ${CRAFT_PART_BUILD}/tmp")# extract jar files into temp folder - spring boot fat jar# contains dependent jars insidecommands.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 jarscommands.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 linkcommands.append("(cd ${CRAFT_PART_INSTALL} && mkdir -p usr/bin && ln -s --relative ${DEST}/bin/java usr/bin/)")returncommands