So, I was playing around with argument parsing in the context of the experimental juliac compilation and trying to use ArgMacros.jl since I have enjoyed its syntax for making CLI scripts in the past.
A couple weeks ago I compiled this script.
using ArgMacros
function parseargs(ARGS)
args = @tuplearguments begin
@argumentdefault Int 3 defaultnum "--default"
@argumentoptional Int mynumber "--number"
@argumentflag isdone "-f"
@argumentflag verbose "-v" "--verbose"
end
return args
end
function (@main)(ARGS)
args = parseargs(ARGS) # segfaults only with mistaken usage
println(Core.stdout, args.defaultnum)
println(Core.stdout, args.mynumber)
println(Core.stdout, args.isdone)
println(Core.stdout, args.verbose)
return 0
end
# pattern copied from https://github.com/MasonProtter/juliac-bench
Base.@ccallable function main(argc::Cint, argv::Ptr{Ptr{UInt8}})::Cint
args = Vector{String}(undef, argc - 1)
for i = 2:argc
argptr = unsafe_load(argv, i)
arg = unsafe_string(argptr)
args[i-1] = arg
end
main(args)
end
Base.Experimental.entrypoint(main, (Cint, Ptr{Ptr{UInt8}}))
It compiled. And it would work when passing arguments as expected, though it would segfault if any unexpected arguments or flags were passed. My impression is that it was trying to generate the usage documentation but during the compilation a needed function had been trimmed out. I’m assuming because the compiler can’t see the error case?
initial segfault
% ./builds/testexe --number 5 --unexpected
[50862] signal 11 (1): Segmentation fault: 11
in expression starting at none:0
jl_lookup_generic_ at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-grannysmith-C07ZQ07FJYVY.0/build/default-grannysmith-C07ZQ07FJYVY-0/julialang/julia-master/src/gf.c:3480 [inlined]
ijl_apply_generic at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-grannysmith-C07ZQ07FJYVY.0/build/default-grannysmith-C07ZQ07FJYVY-0/julialang/julia-master/src/gf.c:3553
jl_apply at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-grannysmith-C07ZQ07FJYVY.0/build/default-grannysmith-C07ZQ07FJYVY-0/julialang/julia-master/src/./julia.h:2244 [inlined]
jl_type_infer at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-grannysmith-C07ZQ07FJYVY.0/build/default-grannysmith-C07ZQ07FJYVY-0/julialang/julia-master/src/gf.c:395
jl_compile_method_internal at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-grannysmith-C07ZQ07FJYVY.0/build/default-grannysmith-C07ZQ07FJYVY-0/julialang/julia-master/src/gf.c:2887
_jl_invoke at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-grannysmith-C07ZQ07FJYVY.0/build/default-grannysmith-C07ZQ07FJYVY-0/julialang/julia-master/src/gf.c:3361 [inlined]
ijl_apply_generic at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-grannysmith-C07ZQ07FJYVY.0/build/default-grannysmith-C07ZQ07FJYVY-0/julialang/julia-master/src/gf.c:3557
println at /Users/ben/projects/sandbox/julia_testscripts/builds/testexe (unknown line)
_quit_try_help at /Users/ben/projects/sandbox/julia_testscripts/builds/testexe (unknown line)
parseargs at /Users/ben/projects/sandbox/julia_testscripts/builds/testexe (unknown line)
main at /Users/ben/projects/sandbox/julia_testscripts/builds/testexe (unknown line)
main at /Users/ben/projects/sandbox/julia_testscripts/builds/testexe (unknown line)
main at /Users/ben/projects/sandbox/julia_testscripts/builds/testexe (unknown line)
start at /usr/lib/dyld (unknown line)
Allocations: 2 (Pool: 2; Big: 0); GC: 0
zsh: segmentation fault ./builds/testexe --number 5 --unexpected
I just updated to the latest nightly and it still compiles, but now it errors in all cases with
fatal: error thrown and no exception handler available.
Core.MethodError(f=Core.Int64, args=(3,), world=0x0000000000008e45)
jl_method_error_bare at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-macmini-x64-6.0/build/default-macmini-x64-6-0/julialang/julia-master/src/gf.c:2508
jl_method_error at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-macmini-x64-6.0/build/default-macmini-x64-6-0/julialang/julia-master/src/gf.c:2526
jl_lookup_generic_ at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-macmini-x64-6.0/build/default-macmini-x64-6-0/julialang/julia-master/src/gf.c:3644 [inlined]
ijl_apply_generic at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-macmini-x64-6.0/build/default-macmini-x64-6-0/julialang/julia-master/src/gf.c:3670
_converttypeNOT. at /Users/ben/projects/sandbox/julia_testscripts/builds/testexe (unknown line)
parseargs at /Users/ben/projects/sandbox/julia_testscripts/builds/testexe (unknown line)
main at /Users/ben/projects/sandbox/julia_testscripts/builds/testexe (unknown line)
main at /Users/ben/projects/sandbox/julia_testscripts/builds/testexe (unknown line)
main at /Users/ben/projects/sandbox/julia_testscripts/builds/testexe (unknown line)
start at /usr/lib/dyld (unknown line)
./builds/testexe --number 5 0.59s user 0.11s system 108% cpu 0.645 total
Obviously juliac is still experimental, so I am not expecting any kind of immediate solution here. And I also understand if it is too early to comment on some of these questions.
That said, my questions are
- What suggestions are for how to get ArgMacros.jl working with juliac? (This seemed easier a week or two ago, when it was only erroring on unexpected flags)
- does anyone know what changed in juliac to change the scripts behavior?
- Are there broader thoughts or plans for making argument parsing easier with juliac?
- Are there plans to unify the scripting entry point
(@main)(ARGS)
with theBase.Experimental.entrypoint(main, (Cint, Ptr{Ptr{UInt8}}))
?
For those interested the @tuplearguments macro expands to:
@tuplearguments macro expansion
julia> @macroexpand @tuplearguments begin
@argumentdefault Int 3 defaultnum "--default"
@argumentoptional Int mynumber "--number"
@argumentflag isdone "-f"
@argumentflag verbose "-v" "--verbose"
end
:(let
begin
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:32 =#
var"#31#splitargs" = ArgMacros._split_arguments(ARGS)
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:33 =#
var"#32#allowextraargs" = false
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:35 =#
ArgMacros._help_check(var"#31#splitargs", $(Expr(:copyast, :($(QuoteNode(quote
#= REPL[1]:2 =#
#= REPL[1]:2 =# @argumentdefault Int 3 defaultnum "--default"
#= REPL[1]:3 =#
#= REPL[1]:3 =# @argumentoptional Int mynumber "--number"
#= REPL[1]:4 =#
#= REPL[1]:4 =# @argumentflag isdone "-f"
#= REPL[1]:5 =#
#= REPL[1]:5 =# @argumentflag verbose "-v" "--verbose"
end))))))
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:37 =#
begin
#= REPL[1]:2 =#
begin
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:213 =#
var"#34#potential_val"::ArgMacros.Union{ArgMacros.String, ArgMacros.Nothing} = ArgMacros._pop_argval!(var"#31#splitargs", [("--default",)...])
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:215 =#
local defaultnum::Int = ArgMacros._converttype!(ArgMacros.Int, ArgMacros.something(var"#34#potential_val", 3), "--default")
end
#= REPL[1]:3 =#
begin
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:240 =#
var"#36#potential_val"::ArgMacros.Union{ArgMacros.String, ArgMacros.Nothing} = ArgMacros._pop_argval!(var"#31#splitargs", [("--number",)...])
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:242 =#
local mynumber::(Union){Int, Nothing} = if ArgMacros.isnothing(var"#36#potential_val")
ArgMacros.nothing
else
ArgMacros._converttype!(ArgMacros.Int, var"#36#potential_val", "--number")
end
end
#= REPL[1]:4 =#
begin
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:267 =#
local isdone::Bool = ArgMacros._pop_flag!(var"#31#splitargs", [("-f",)...])
end
#= REPL[1]:5 =#
begin
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:267 =#
local verbose::Bool = ArgMacros._pop_flag!(var"#31#splitargs", [("-v", "--verbose")...])
end
end
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:39 =#
if !var"#32#allowextraargs" && !(ArgMacros.isempty(var"#31#splitargs"))
#= /Users/ben/.julia/packages/ArgMacros/JJu6G/src/macros.jl:40 =#
ArgMacros._quit_try_help("Too many or unrecognized arguments provided")
end
end
(defaultnum = defaultnum, mynumber = mynumber, isdone = isdone, verbose = verbose)
end)