Let’s look at what an entry point or main function is. In the most straightforward definition, this is where your program starts execution after loading the necessary code. In many approaches, you denote the start (and end) of the program in a function with a specific signature and name, often “main” or something similar. Your program can’t start in several places at once, so there’s an ahead-of-time (AOT) compiler that looks at all of your code and complains if it finds multiple main functions. This does apply if you were compiling PackageCompiler.jl apps (julia_main()::Cint
) or the upcoming juliac executables (Base.@ccallable main()::Cint
, the last time I read about it).
When you run the julia
command, you’re not AOT-compiling the input code, regardless of whether you’re entering an interactive REPL session or going back to the command line afterward. With the exception of what packages installed in a given environment opted to precompile, Julia parses and executes expressions one by one and compiles native code just-in-time (JIT), on demand. Your program started at the first line and ended at the last line like any of your scripts; the last thing it did was define a (@main)(args)
method you never call and wrap up the module. (Technically (@main)
expands to the simple function name main
, but the macro call also does a critical registration step in the background, so I’ll keep referring to the definition by the macro call). If you just needed to run a program from the command line, you didn’t need the enclosing module, you didn’t need a (@main)
function, and you didn’t need to wrap your primary work in one function to call (though it’s much easier for the JIT compiler to optimize a function call, so it’s often recommended).
So if (@main)
isn’t where the Julia program begins, what is it? Its docstring refers to it as an entry point for the current module, not one for the whole program. The command line interface documentation documents the -m
/--module
flag as running the (@main)
entry point for a package (all packages are modules hence the flag’s name). Each module can have its own (@main)
to implement an application routine of sorts in addition to the usual library routines, and it can run automatically from the command line with the -m
/--module
flag or with a unique import of main
to the Main
module that all Julia programs start in (which is what the -m
flag does for packages). Main.main(ARGS)
executes when the process is about to automatically exit
, so this arguably isn’t an “entry point” at all, but that’s the term we have to live with.
This is and likely will remain a very rarely used feature because it saves negligible effort compared to any explicit function call, including hppt_server_julia.main(ARGS)
. The only way to entirely avoid an explicit function call or import statement in Main
is the -m
flag or defining (@main)
right in Main
of the input file or expression at the command line, and that import statement can otherwise become about as long because packages on principle won’t export a common or conventional name like main
via bare using
statements. It is really only convenient for the -m
flag, but I’ve never ran across a package to use it for and it doesn’t even seem available for me (v1.11.5, Windows).
You might be going back to this question at this point because your script only has the one (@main)
, so shouldn’t Julia be able to automatically identify this and run it for you, even if the program doesn’t really start there? At least 2 problems: 1) Julia doesn’t have an AOT compiler by default to even try to identify and enforce one (@main)
definition, and 2) multiple coexisting modules allow multiple (@main)
by design. If you import a package with a (@main)
before your own, then include
another module file with a (@main)
, there’s no good reason to automatically call your (@main)
instead of the others. A definition or import into Main
is crucial for stable selection.