Julia 应用程序的编译版本在运行时出现 MethodError,否则运行良好

MethodError at runtime for compiled version of a Julia application that otherwise runs fine

我有一个运行良好的 Julia 应用程序,但我想制作一个已编译且更易于分发的版本。据我了解,这是 PackageCompiler.

的工作

我能够构建可执行文件,并且在询问它 --help 时它运行良好,但是当我想要它做一些实际工作(处理数据)时,在一些初始信息输出之后,它崩溃并显示以下错误消息:

fatal: error thrown and no exception handler available.
MethodError(f=typeof(Base.convert)(), args=(Int32, nothing), world=0x00000000000065d9)
rec_backtrace at /home/bli/src/julia/src/stackwalk.c:94
record_backtrace at /home/bli/src/julia/src/task.c:219 [inlined]
jl_throw at /home/bli/src/julia/src/task.c:429
jl_method_error_bare at /home/bli/src/julia/src/gf.c:1606
jl_method_error at /home/bli/src/julia/src/gf.c:1624
jl_apply_generic at /home/bli/src/julia/src/gf.c:2161
julia_main at /home/bli/src/qaf_demux/Julia/QafDemux/bin/qaf_demux_to_compile.jl:12
julia_main at /home/bli/src/qaf_demux/Julia/QafDemux/deps/builddir/qaf_demux.so (unknown line)
main at ./deps/builddir/qaf_demux (unknown line)
__libc_start_main at /build/glibc-LK5gWL/glibc-2.23/csu/../csu/libc-start.c:291
_start at ./deps/builddir/qaf_demux (unknown line)

这个MethodError是什么意思?我可以从回溯中得到什么有用的信息?

这是用于编译它的 deps/build.jl 脚本:

import Pkg
Pkg.add("ArgParse")
Pkg.add("IterTools")
Pkg.add("FASTX")
Pkg.add("CodecZlib")
Pkg.add("REPL")
Pkg.add("PackageCompiler")
using PackageCompiler
build_executable(joinpath(@__DIR__, "../bin/qaf_demux_to_compile.jl"), "qaf_demux", snoopfile=joinpath(@__DIR__, "../bin/snoop.jl"))

snoop.jl 使用一些测试命令行调用我的 QafDemux 包的主要功能(对应于用于测试已编译可执行文件的命令行):

#!/usr/bin/env julia

push!(LOAD_PATH, abspath(joinpath(@__DIR__, "../src/")))
import QafDemux
const qd = QafDemux

test_args = "-i ../../test_data/TCR_ampli_R1_50k.fastq.gz -o test_run -b GCAGAGATAAGC GCAGAGATGCAC GCAGAGACTCAG GCAGAGAGGAAT GCAGAGACGAGG GCAGAGAAGGAG GCAGAGATGTTG GCAGAGACAACT GCAGAGAGGCTA GCAGAGAGAATG GCAGAGACCAAC GCAGAGAGAGAC -s 3 -m 2 -p 0.1"
qd.main(split(test_args))

qaf_demux_to_compile.jl,根据PackageCompiler说明,如下:

#!/usr/bin/env julia

push!(LOAD_PATH, abspath(joinpath(@__DIR__, "../src/")))
import QafDemux
const qd = QafDemux

Base.@ccallable function julia_main(ARGS::Vector{String})::Cint
    qd.main()
end

未编译版本,运行良好,几乎相同:

#!/usr/bin/env julia

push!(LOAD_PATH, abspath(joinpath(@__DIR__, "../src/")))
import QafDemux
const qd = QafDemux

qd.main()

(在 https://discourse.julialang.org/t/understanding-runtime-errors-with-packagecompiler-built-executables/29120/2 调查的较早阶段交叉发布)


编辑:工作解决方案

根据@Anshul Singvi的建议,我修改了我的QafDemux模块的主要功能,现在它return是一个整数,并且使用是这样的也是传递给PackageCompiler.build_executable.

的代码中julia_main函数的return值

bin/qaf_demux_to_compile.jl:

#!/usr/bin/env julia

push!(LOAD_PATH, abspath(joinpath(@__DIR__, "../src/")))
import QafDemux
const qd = QafDemux

Base.@ccallable function julia_main(ARGS::Vector{String})::Cint
    return qd.main()
end

(我还必须更改 main 函数,以便它接受可选的命令行,否则 snoopfile 实际上是错误的)。

查看堆栈跟踪,您可以看到 Julia 正在尝试将 nothing 转换为 Int32,在行中:

MethodError(f=typeof(Base.convert)(), args=(Int32, nothing), world=0x00000000000065d9)

这是因为您的 julia_main 函数没有明确地 return 任何东西,而且 qd.main 似乎也没有 return 任何东西。在没有明确的 return 值的情况下,Julia 默认 return 什么都不做。

但是,在您的方法的类型约定中,指定 julia_main 必须 return 一个 Cint,这在您的系统上似乎成为 Int32。因此,Julia 试图将 julia_main(隐含地)return 转换为 Int32——这是不可能的!

要解决此问题,您只需确保 return 是一个整数。更好的做法是:

Base.@ccallable function julia_main(ARGS::Vector{String})::Cint
    try
        qd.main()
    catch e
        print(stderr, e) # print the error to standard error
        return 1 # in command line tools, a return value of 1 means error
    finally
        return 0 # similarly, a return value of 0 means that the program ran properly
    end
end