Skip to content

Commit

Permalink
Introduce -m/--module flag to execute a main function in a package
Browse files Browse the repository at this point in the history
This aims to bring similar functionality to Julia as the `-m` flag for Python which exists to directly run some function in a package and being able to pass arguments to that function.

While in Python, `python -m package args` runs the file `<package>.__main__.py`, the equivalent Julia command (`julia -m Package args`) instead runs `Package.main(args)`. The package is assumed to be installed in the environment `julia` is run in.

An example usage could be:

Add the package:
```
(@v1.11) pkg> add https://github.com/KristofferC/Rot13.jl
     Cloning git-repo `https://github.com/KristofferC/Rot13.jl`
    Updating git-repo `https://github.com/KristofferC/Rot13.jl`
   Resolving package versions...
    Updating `~/.julia/environments/v1.11/Project.toml`
  [43ef800a] + Rot13 v0.1.0 `https://github.com/KristofferC/Rot13.jl#master`
    Updating `~/.julia/environments/v1.11/Manifest.toml`
  [43ef800a] + Rot13 v0.1.0 `https://github.com/KristofferC/Rot13.jl#master`
```

And then it can be run (since it has a `main` function) via:

```
❯ ./julia/julia -m Rot13 "encrypt this for me" "and this as well"
rapelcg guvf sbe zr
naq guvf nf jryy
```
  • Loading branch information
KristofferC authored and KristofferC committed Dec 15, 2023
1 parent 9fc1b65 commit b61118f
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 2 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Command-line option changes
This is intended to unify script and compilation workflows, where code loading may happen
in the compiler and execution of `Main.main` may happen in the resulting executable. For interactive use, there is no semantic
difference between defining a `main` function and executing the code directly at the end of the script ([50974]).
* The `-m/--module` flag can be passed to run the `main` function inside a package with a set of arguments.

Multi-threading changes
-----------------------
Expand Down
9 changes: 8 additions & 1 deletion base/client.jl
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ function exec_options(opts)
if cmd_suppresses_program(cmd)
arg_is_program = false
repl = false
elseif cmd == 'L'
elseif cmd == 'L' || cmd == 'm'
# nothing
elseif cmd == 'B' # --bug-report
# If we're doing a bug report, don't load anything else. We will
Expand Down Expand Up @@ -292,6 +292,13 @@ function exec_options(opts)
elseif cmd == 'E'
invokelatest(show, Core.eval(Main, parse_input_line(arg)))
println()
elseif cmd == 'm'
@eval Main import $(Symbol(arg)).main
if !should_use_main_entrypoint()
error("`main` in `$arg` not declared as entry point (using `@main`)")
end
return false

elseif cmd == 'L'
# load file immediately on all processors
if !distributed_mode
Expand Down
5 changes: 5 additions & 0 deletions doc/man/julia.1
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ Enable or disable incremental precompilation of modules
-e, --eval <expr>
Evaluate <expr>

.TP
-m, --module <Package> [args]
Run entry point of `Package` (`@main` function) with `args'.


.TP
-E, --print <expr>
Evaluate <expr> and display the result
Expand Down
11 changes: 10 additions & 1 deletion src/jloptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ static const char opts[] =
// actions
" -e, --eval <expr> Evaluate <expr>\n"
" -E, --print <expr> Evaluate <expr> and display the result\n"
" -m, --module <Package> [args]\n"
" Run entry point of `Package` (`@main` function) with `args'.\n"
" -L, --load <file> Load <file> immediately on all processors\n\n"

// parallel options
Expand Down Expand Up @@ -261,7 +263,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
opt_gc_threads,
opt_permalloc_pkgimg
};
static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:";
static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:m:";
static const struct option longopts[] = {
// exposed command line options
// NOTE: This set of required arguments need to be kept in sync
Expand All @@ -274,6 +276,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
{ "banner", required_argument, 0, opt_banner },
{ "home", required_argument, 0, 'H' },
{ "eval", required_argument, 0, 'e' },
{ "module", required_argument, 0, 'm' },
{ "print", required_argument, 0, 'E' },
{ "load", required_argument, 0, 'L' },
{ "bug-report", required_argument, 0, opt_bug_report },
Expand Down Expand Up @@ -411,6 +414,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
case 'e': // eval
case 'E': // print
case 'L': // load
case 'm': // module
case opt_bug_report: // bug
{
size_t sz = strlen(optarg) + 1;
Expand All @@ -424,6 +428,10 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
ncmds++;
cmds[ncmds] = 0;
jl_options.cmds = cmds;
if (c == 'm') {
optind -= 1;
goto parsing_args_done;
}
break;
}
case 'J': // sysimage
Expand Down Expand Up @@ -858,6 +866,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
"This is a bug, please report it.", c);
}
}
parsing_args_done:
jl_options.code_coverage = codecov;
jl_options.malloc_log = malloclog;
int proc_args = *argcp < optind ? *argcp : optind;
Expand Down
5 changes: 5 additions & 0 deletions test/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1406,3 +1406,8 @@ end
end
end
end

@testset "-m" begin
rot13proj = joinpath(@__DIR__, "project", "Rot13")
@test readchomp(`$(Base.julia_cmd()) --startup-file=no --project=$rot13proj -m Rot13 --project nowhere ABJURER`) == "--cebwrpg abjurer NOWHERE "
end
3 changes: 3 additions & 0 deletions test/project/Rot13/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name = "Rot13"
uuid = "43ef800a-eac4-47f4-949b-25107b932e8f"
version = "0.1.0"
15 changes: 15 additions & 0 deletions test/project/Rot13/src/Rot13.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Rot13

function rot13(c::Char)
shft = islowercase(c) ? 'a' : 'A'
isletter(c) ? c = shft + (c - shft + 13) % 26 : c
end

rot13(str::AbstractString) = map(rot13, str)

function (@main)(ARGS)
foreach(arg -> print(rot13(arg), " "), ARGS)
return 0
end

end # module Rot13

0 comments on commit b61118f

Please sign in to comment.