# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-## Copyright 2020-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/>."""Plugin base class and definitions."""from__future__importannotationsimportloggingimportosimportsubprocessimporttempfilefromoverridesimportoverridefrom.baseimportPluginlogger=logging.getLogger(__name__)
[docs]classJavaPlugin(Plugin):"""A base class for java-related plugins. Provide common methods to deal with the java executable location and symlink creation. """def_check_java(self,javac:str)->tuple[int|None,str]:withtempfile.TemporaryDirectory()astempdir:test_class=""" public class Test { public static void main(String[] args){ System.out.println(System.getProperty("java.specification.version")); } }"""withopen(f"{tempdir}/Test.java","w")asfile:file.write(test_class)try:subprocess.call([javac,"-d",tempdir,f"{tempdir}/Test.java"])java_home=os.path.dirname(os.path.dirname(javac))spec_version=subprocess.check_output([java_home+"/bin/java","-cp",tempdir,"Test"],text=True)# Java 8 reports spec 1.8. Treat it as a spec version 8# 11 and up report the actual spec version numberversion=int(spec_version.split(".")[-1])exceptsubprocess.CalledProcessErroraserr:logging.debug(f"{javac} is not a valid Java compiler: {err.output}")exceptPermissionErroraserr:logging.debug(f"{javac} is not a valid Java compiler. Permission error {err}")else:logging.info(f"Found JAVA_HOME {java_home}. Java version {version}.")returnversion,java_homelogging.info("JDK not found.")returnNone,""def_find_javac(self)->list[str]:cmd=["find","/usr/lib/jvm","-path","*/bin/javac","-print"]output=subprocess.check_output(cmd,text=True)return[xforxinoutput.split("\n")iflen(x)>0]
[docs]@overridedefget_build_environment(self)->dict[str,str]:"""Override JAVA_HOME in the build environment."""env={}candidate_java={}forjavacinself._find_javac():spec,home=self._check_java(javac)ifspecisnotNone:candidate_java[spec]=homeiflen(candidate_java)>0:best=sorted(candidate_java.keys())[-1]env["JAVA_HOME"]=candidate_java[best]returnenv
def_get_java_link_commands(self)->list[str]:"""Get the bash commands to provide /bin/java symlink."""# pylint: disable=line-too-longreturn['# Find the "java" executable and make a link to it in $CRAFT_PART_INSTALL/bin/java',"mkdir -p ${CRAFT_PART_INSTALL}/bin","java_bin=$(find ${CRAFT_PART_INSTALL} -name java -type f -executable)","ln -s --relative $java_bin ${CRAFT_PART_INSTALL}/bin/java",]# pylint: enable=line-too-longdef_get_jar_link_commands(self)->list[str]:"""Get the bash commands to provide ${CRAFT_STAGE}/jars."""# pylint: disable=line-too-longreturn["# Find all the generated jars and hardlink them inside CRAFT_PART_INSTALL/jar/","mkdir -p ${CRAFT_PART_INSTALL}/jar",r'find ${CRAFT_PART_BUILD}/ -iname "*.jar" -exec ln {} ${CRAFT_PART_INSTALL}/jar \;',]# pylint: enable=line-too-longdef_get_java_post_build_commands(self)->list[str]:"""Get the bash commands to structure a Java build in the part's install dir. :return: The returned list contains the bash commands to do the following: - Create bin/ and jar/ directories in ${CRAFT_PART_INSTALL}; - Find the ``java`` executable (provided by whatever jre the part used) and link it as ${CRAFT_PART_INSTALL}/bin/java; - Hardlink the .jar files generated in ${CRAFT_PART_BUILD} to ${CRAFT_PART_INSTALL}/jar. """returnself._get_java_link_commands()+self._get_jar_link_commands()