为不同平台创建独立的Java可执行文件,无需安装

13

我使用jlink创建了一个Java应用程序运行时镜像。我希望能够将软件作为可执行文件传送到不同的平台(最好是通过在一个平台上进行构建,如交叉编译)。

理想情况下,这将是一个单个的应用程序文件,用户可以双击启动,而无需安装任何东西。

如何实现这一目标?


你可能应该查看jpackagerjlink也可以做你想要的。 - ZhekaKozlov
请查看 warp-packer。我的博客文章 Write Once, Build Anywhere 描述了一种解决方案。有关更多详细信息,请参见我的文本编辑器的 installer 脚本。MacOS 也应该是可行的。releases 页面分发自包含的 Windows 和 Linux 二进制文件。无需安装程序:双击即可运行。 - Dave Jarvis
4个回答

5
您所描述的是所谓的本地可执行文件。有些程序可以将您的Java应用程序封装成可执行文件,但由于Java在Java虚拟机(JVM)上运行其代码,因此用户需要预先安装它才能使您的程序即开即用。您可以使用类似C++或C#的语言为您的应用程序编写安装程序(C#在.NET Runtime上运行,所有Windows机器都预装了它),安装JVM并可能将您的应用程序与之一起安装,然后将该代码编译为本地可执行文件。这样,最终用户就不需要寻找Java下载了。我认为Minecraft采用了这种方法。
使用以下任何工具将您的Java可执行文件封装为本地可执行文件:

据我所知,基本上是正确的。没有办法“制作可执行文件”,始终需要JVM。自动化一些安装JVM和任何所需库的步骤的安装程序是OP能够接近的。 - markspace
@Strom 是的,Launch4j 只能制作在 Windows 上运行的 exe 文件。类似于这样可以制作 Mac 可执行文件(DMJ 文件)的工具可能已经存在了。 - DudeManGuy
您的帖子提供了非常有用的信息,介绍了如何打包Java应用程序,使其能够在Windows和Mac OS上双击运行,而无需安装JRE运行时。 - user10316640
我有一个便携式的Java和Python发行版。如果我只使用相对路径来制作程序,那么你可以简单地复制整个包含可执行文件的文件夹树,并在任何x64 Windows操作系统上运行它。我在工作中使用USB。这很方便,特别是与Python和PIP一起使用。但是,谁想要一个几GB的文件夹呢?他们真的只需要安装运行时库就可以了。 - RoyM
@RoyM,Java在不同版本之间已经变得不兼容,并且一次只能有一个版本在系统范围内处于活动状态。这导致一些程序可以运行而其他程序会失败。通过使用Jlink,这些程序可以在任何安装的版本上运行。 - user10316640

2
另外,请看一下SubstrateVM。虽然它不是真正的Java,但在某些情况下(如简单的命令行应用程序)可能会对您有所帮助。

Substrate VM是一个框架,允许在封闭世界假设下预先编译Java应用程序为可执行映像或共享对象(ELF-64或64位Mach-O)。

最初的回答已翻译完毕,以上是需要翻译的内容。

1
是的,从Java 8开始,有两种方法可以实现这一点,一种是使用javapackager工具,另一种是使用JavaFX Ant任务(它们实际上并不特定于JavaFX,并且随Java 8 JDK一起提供)。
以下是一个将应用程序打包为Jar文件的示例,设置主类和类路径属性,并使用Maven将所有依赖项复制到文件夹中。
<plugins>
        <plugin>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}/lib</outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.2.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <mainClass>${project.groupId}.${project.artifactId}.DemoCLI</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
</plugins>

以下是用于打包独立应用程序的 Ant 构建文件(Windows 上的 exe,OS X 上的 .app 和 .dmg,Linux 上的 .deb 和 .rpm)。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>

<!--
 Uses the JavaFX Ant Tasks to build native application bundles
 (specific to the platform which it is built on).

 See https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/javafx_ant_tasks.html

 These tasks are distributed with version 8 of the Oracle JDK,
 Amazon Corretto JDK, RedHat JDK, probably others, though they
 do not seem to be the OpenJDK on Debian Linux

 -->
<project
    name="fxwebclient" default="default" basedir="."
    xmlns:fx="javafx:com.sun.javafx.tools.ant">

    <!-- In Java 8, java.home is typically the JRE of a JDK -->
    <property name="jdk.lib.dir" value="${java.home}/../lib" />

    <!-- Where to build our app bundles -->
    <property name="build.dist.dir" value="${basedir}/target/dist" />

   <echo>Using Java from ${java.home}</echo>

    <target name="default" depends="clean">

        <!-- get the ant-jfx.jar from the JDK -->
        <taskdef resource="com/sun/javafx/tools/ant/antlib.xml"
            uri="javafx:com.sun.javafx.tools.ant"
            classpath="${jdk.lib.dir}/ant-javafx.jar" />

        <!-- Define our application entry point -->
        <fx:application id="demo" name="demo"
            mainClass="yourpackage.DemoCLI" />

        <!-- Our jar and copied dependency jars (see pom.xml) -->
       <fx:resources id="appRes">
            <fx:fileset dir="${basedir}/target" includes="*.jar"/>
            <fx:fileset dir="${basedir}/target/lib" includes="*.jar" />
       </fx:resources>

        <!-- Create app bundles [platform specific] -->
        <fx:deploy nativeBundles="all"
            outdir="${build.dist.dir}" outfile="DemoWebClient">

            <fx:application refid="demo" />
            <fx:resources refid="appRes" />

        </fx:deploy>

    </target>

    <!-- clean up -->
    <target name="clean">
        <delete dir="${build.dist.dir}" includeEmptyDirs="true" />
        <delete file="${basedir}/target/${ant.project.name}.jar" />
    </target>

</project>

0

双击可在多个平台上运行可执行文件,需要先在操作系统中注册文件类型,或者已知文件类型以了解如何处理代码。

jlink将“所需模块及其传递依赖项”静态链接到输出中。

这个问题没有跨平台的解决方案。

将所有平台包含在一个单一文件中是不可能的(或者换句话说,不可行的),因为每种可执行文件类型(COFF、ELF...)都有不同的结构。您可以尝试使用通用批处理文件启动适当的可执行文件,但在Windows上,这将需要文本文件类型编码;从而污染剩余的二进制代码。

使用jlink 新的jmod文件格式将允许您将本地代码存储在Java容器中,从而允许在单个可执行映像中输入嵌入式本机JRE代码的入口点为单个预定义平台。

这个问题的另一面是安全性问题。由于嵌入式JRE不受安全更新的影响,黑客可能选择嵌入先前已知的有缺陷的JRE,从而向不知情的消费者暴露已纠正的漏洞。

反病毒程序的预期响应将是将所有未更新的嵌入式JRE标记为病毒。


1
我不会称之为“静态链接”,而更多地是将所有内容打包到一个文件中。 - Robert
2
实际上,jlink 允许在没有安装 Java 的情况下运行应用程序。这是它的主要目的。 - ZhekaKozlov
@ZhekaKozlov,如果这是它的“主要目的”,为什么没有直接针对哪个操作系统的选项?没有嵌入本地代码的jmod文件,仅使用jlink无法创建本机可执行文件。 - user10316640
@Robert,“静态链接是链接器将程序中使用的所有库例程复制到可执行映像中的结果。这可能需要比动态链接更多的磁盘空间和内存,但是由于不需要在其运行的系统上存在库,因此更快速且更可移植。”引用自“https://kb.iu.edu/d/akqn”。既然每个官方Java实现都一直使用Run-time / dynamic linking直到jlink,你会怎么称呼它? - user10316640
@Strom 这只是打包:一个可执行文件和数据,位于可执行文件结束后,就像自解压安装程序一样。静态链接集成了一个链接器,但这里只是将数据放在可执行文件之外,但在同一个文件中。 - Robert
@Strom 静态链接 Java 字节码!!?我认为在你发布更多这样令人困惑的声明之前,我们应该停止。 - Robert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接