使用 Launch4J 构建的 Micronaut 应用程序未启动

发布于 2025-01-16 20:43:50 字数 3555 浏览 5 评论 0原文

我已经成功地将我的桌面应用程序从 Dagger/Spring 移植到 Micronaut/Spring(其中有太多 Spring 代码,我无法及时将其全部删除)。使用 Micronaut 和 PicoCLI,一切都在 Eclipse/Gradle 中完美运行。它正在启动,测试正在通过,所以现在我想要进行构建并安装它,以确保一切都适合我们的客户。

我们的构建(通过 Gradle)使用 Shadow (com.github.johnrengelman.shadow) 将我们的应用程序及其所有依赖项合并到一个 Jar 文件中。我已经检查过,所有预期的依赖项似乎都存在,并且从外观上看,生成的 Micronaut 类也存在于影子 jar 中。然后我们使用 Launch4J 使这个影子 jar 可执行。据我所知,Launch4J 的输出看起来是正确的,但是当我尝试运行已安装的应用程序时,启动屏幕会出现几分之一秒,仅此而已。当我将 Launch4J 生成的可执行文件作为 jar 文件运行时,我得到以下信息:

Error: An unexpected error occurred while trying to open file My App.exe

We're running with OpenJDK11 (11.0.2) and generated a JRE as part of the gradle build

runtime {
    modules = [
        'java.base',
        'java.compiler',
        'java.datatransfer',
        'java.desktop',
        'java.instrument',
        'java.logging',
        'java.management',
        'java.naming',
        'java.prefs',
        'java.rmi',
        'java.scripting',
        'java.security.sasl',
        'java.sql',
        'java.transaction.xa',
        'java.xml',
        'jdk.compiler',
        'jdk.httpserver',
        'jdk.javadoc',
        'jdk.naming.rmi',
        'jdk.unsupported'
    ]
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
}

我已经能够收集所有的 Launch4J 调试信息看起来是正确的(除了退出代码 1 之外),到目前为止,我对此进行深入研究的所有尝试都被证明是徒劳的。我发现这种类型错误的最常见原因是 jar 中的 MANIFEST.MD 无效(我已经检查过,它与原始的 Micronaut 之前的清单相同),或者类路径问题(影子 jar 应该包含所有据我所知,没有使用所需的依赖项,并且没有使用其他类路径引用)。 build.gradle 的相关部分如下:

task createExe(type: Launch4jLibraryTask) {
    jarTask = shadowJar
    icon = "${projectDir}/src/main/installer/nsis/Icon.ico"
    outfile = "${appName}.exe"
    copyConfigurable = files {}
    stayAlive = 'true'
    bundledJrePath = 'jre'
    bundledJre64Bit = true
    jvmOptions = [
            '-Xmx1024m',
            '-Dsun.java2d.d3d=false'
    ]
    fileDescription = appName + ' Application'
    productName = appName
    internalName = appName
    copyright = 'My Company'
    companyName = 'My Company'
}

jar {
    manifest {
        attributes(
                'Main-Class': mainClassName,
                'SplashScreen-Image': 'images/Z006BASSPS.jpg'
        )
    }
}

shadowJar {
    dependsOn replaceTokens
    zip64 true
    mergeServiceFiles() {
        exclude 'META-INF/services/org.xmlpull.v1.XmlPullParserFactory'
    }
    append 'META-INF/spring.factories'
    append 'META-INF/spring.handlers'
    append 'META-INF/spring.schemas'
    append 'META-INF/spring.tooling'
}

自 Micronaut 出现之前一切正常运行以来,这些部分均未发生任何更改。

我尝试过使用它生成的 JRE 来启动构建生成的影子 jar,看起来它正在工作(或者至少,它正在执行并且不会立即死亡)。因此,我相当有信心问题出在构建的 Launch4J 部分。

Micronaut 与 Launch4j 兼容吗?是否缺少某些内容,或者可以尝试其他内容来查看可能发生的情况?我真的很困惑,一切看起来都应该有效,但事实并非如此......

更新:

经过大量挖掘后,我开始意识到问题出在 Launch4j 上。此构建生成的 Shadow jar 运行完美。然而,通过 Launch4j 运行工作 Jar 会生成一个可执行文件,该可执行文件仅生成

Error: An unexpected error occurred while trying to open file launch4j\MyApp.exe

我创建了一个虚拟 Micronaut 应用程序,并使用 Launch4j 生成它的构建版本似乎也完全按预期工作,所以我不认为这是 Launch4j - Micronaut 不兼容每说。

有没有办法从可执行文件中获取更多内容?有什么实际可行的事情吗?我尝试将 headerType 从 gui 更改为控制台(根据我在这里看到的其他 Launch4j 问题),但这没有任何影响。我尝试使用 --l4j-debug(-all) 运行,并将其与升级前的输出进行比较,生成的日志文件中没有任何有意义的更改。

更新:

我相信我已经缩小了问题的范围:如果 jar 中有太多文件,launch4j 似乎不起作用。我认为限制是 65535,因为这是 Shadow 需要 zip64 标志时的限制。在我的小型虚拟应用程序中,我将 70,000 个文本文件添加到资源目录中,这些文件就位后,它将不再运行(如上所述)。一旦我将文本文件的数量减少到 50,000 个,一切就重新开始运行。我不确定每个说法的限制是否为 65535(但这是我的期望),但一切似乎都指向它。

I have successfully ported my desktop application from Dagger/Spring to Micronaut/Spring (there's just way too much Spring code in there for me to be able strip it all out in a timely manner). Using Micronaut and PicoCLI everything is working beautifully in Eclipse/Gradle. It's launching, tests are passing, so now I want to do a build and install it to make sure that everything will work for our customer.

Our build (via Gradle) uses Shadow (com.github.johnrengelman.shadow) to combine our application with all of its dependencies into a single Jar file. I've checked and all expected dependencies appear to be present, and from the look of it the generated Micronaut classes are also present in the shadow jar. We then use Launch4J to make this shadow jar executable. From everything that I can tell, the output from Launch4J looks to be correct, however when I try to run the installed application, the splash screen appears for a fraction of a second and that's it. When I run the Launch4J generated executable as a jar file, I get the following:

Error: An unexpected error occurred while trying to open file My App.exe

We're running with OpenJDK11 (11.0.2) and generating a JRE as part of the gradle build

runtime {
    modules = [
        'java.base',
        'java.compiler',
        'java.datatransfer',
        'java.desktop',
        'java.instrument',
        'java.logging',
        'java.management',
        'java.naming',
        'java.prefs',
        'java.rmi',
        'java.scripting',
        'java.security.sasl',
        'java.sql',
        'java.transaction.xa',
        'java.xml',
        'jdk.compiler',
        'jdk.httpserver',
        'jdk.javadoc',
        'jdk.naming.rmi',
        'jdk.unsupported'
    ]
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
}

The Launch4J debug information I've been able to collect all looks correct (other than an Exit Code of 1), and all of my attempts at digging into this have proven to be fruitless thus far. I gather that the most common causes of this type of error are invalid MANIFEST.MD in the jar (I've checked and it's identical to the original pre-Micronaut manifest), or issues with the classpath (the shadow jar should contain all of the required dependencies, and no other classpath references are used as far as I can tell). The relevant portions of the build.gradle are as follows:

task createExe(type: Launch4jLibraryTask) {
    jarTask = shadowJar
    icon = "${projectDir}/src/main/installer/nsis/Icon.ico"
    outfile = "${appName}.exe"
    copyConfigurable = files {}
    stayAlive = 'true'
    bundledJrePath = 'jre'
    bundledJre64Bit = true
    jvmOptions = [
            '-Xmx1024m',
            '-Dsun.java2d.d3d=false'
    ]
    fileDescription = appName + ' Application'
    productName = appName
    internalName = appName
    copyright = 'My Company'
    companyName = 'My Company'
}

jar {
    manifest {
        attributes(
                'Main-Class': mainClassName,
                'SplashScreen-Image': 'images/Z006BASSPS.jpg'
        )
    }
}

shadowJar {
    dependsOn replaceTokens
    zip64 true
    mergeServiceFiles() {
        exclude 'META-INF/services/org.xmlpull.v1.XmlPullParserFactory'
    }
    append 'META-INF/spring.factories'
    append 'META-INF/spring.handlers'
    append 'META-INF/spring.schemas'
    append 'META-INF/spring.tooling'
}

None of which has been changed since pre-Micronaut when everything was working.

I've tried launching shadow jar produced by the build with the JRE it produces, and that looks like it's working (or at least, it's being executed and not dying instantly). So I'm fairly confident that the issue is on the Launch4J portion of the build.

Is Micronaut compatible with Launch4j? Is there something that is missing, or something else that can be tried to see what could be going on? I'm genuinely stumped, everything looks like it should work, but it's not...

Update:

After doing a bunch of digging, I've come to realize that the issue is with Launch4j. The shadow jar produced by this build is working perfectly. And yet running the working Jar through Launch4j results in an executable that only produces

Error: An unexpected error occurred while trying to open file launch4j\MyApp.exe

I created a dummy Micronaut application and producing a build of it with Launch4j also seems to work exactly as expected, so I don't think that it's a Launch4j - Micronaut incompatibility per-say.

Is there any way to get something more from the executable? Something that is actually actionable? I've tried changing the headerType from gui to console (per other Launch4j questions I've seen on here), but that had no impact. I've tried running with --l4j-debug(-all), and compared it with the output from before the upgrade, and there is no meaningful change in the produced log file.

Update:

I believe that I've narrowed down what the problem is: launch4j doesn't seem to work if there are too many files in the jar. I presume that the limit is 65535, as that's the limit for when Shadow requires the zip64 flag. In my tiny dummy application I added 70,000 text files into the resources directory and with those files in place it would no longer run (as per what was described above). Once I dropped the number of text files to 50,000 everything started running again. I don't know for sure whether the limit is 65535 per say (but that is my expectation), however everything seems to point to it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

谎言月老 2025-01-23 20:43:50

我还没有找到任何关于使用 launch4j 处理超过 65535 个文件的信息(即:像shadowJar zip64 标志之类的东西),也没有找到任何关于这方面的信息。然而,对我有用的一种解决方案是将 dontWrapJar 设置为 true。这将创建一个小型启动器,用于使用捆绑的 JRE 运行创建的 jar 文件,而不是转换整个 jar,从而将所有(或至少大部分)文件保留在 launch4j 生成的可执行文件之外。更新后的 gradle 任务如下

task createExe(type: Launch4jLibraryTask) {
    jarTask = shadowJar
    dontWrapJar true // <<<< THIS IS THE ADDED LINE
    icon = "${projectDir}/src/main/installer/nsis/Icon.ico"
    outfile = "${appName}.exe"
    copyConfigurable = files {}
    stayAlive = 'true'
    bundledJrePath = 'jre'
    bundledJre64Bit = true
    jvmOptions = [
            '-Xmx1024m',
            '-Dsun.java2d.d3d=false'
    ]
    fileDescription = appName + ' Application'
    productName = appName
    internalName = appName
    copyright = 'My Company'
    companyName = 'My Company'
}

除此之外,唯一的其他更改是更新打包过程,以将生成的影子 jar 合并到生成的安装程序的 lib 目录中。

I haven't been able to find any information for using launch4j with more than 65535 files (i.e.: something like the shadowJar zip64 flag), nor really much of any information in this regard in general for that matter. However one solution that works for me, is to set dontWrapJar to true. This creates a tiny launcher for running the created jar file with the bundled JRE, rather than converting the entire jar, keeping all (or at least the majority of) the files out of the launch4j generated executable. The updated gradle task is as follows

task createExe(type: Launch4jLibraryTask) {
    jarTask = shadowJar
    dontWrapJar true // <<<< THIS IS THE ADDED LINE
    icon = "${projectDir}/src/main/installer/nsis/Icon.ico"
    outfile = "${appName}.exe"
    copyConfigurable = files {}
    stayAlive = 'true'
    bundledJrePath = 'jre'
    bundledJre64Bit = true
    jvmOptions = [
            '-Xmx1024m',
            '-Dsun.java2d.d3d=false'
    ]
    fileDescription = appName + ' Application'
    productName = appName
    internalName = appName
    copyright = 'My Company'
    companyName = 'My Company'
}

Other than that, the only other change was updating the packaging process to incorporate the generated shadow jar in a lib directory in the produced installer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文