Skip to content

Commit

Permalink
Introduce -m/--module flag to execute a main function in a package (J…
Browse files Browse the repository at this point in the history
…uliaLang#52103)

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:
```julia
(@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
```

I'm not sure if `-m/--module` is the best choice but perhaps the
association to Python makes it worth it.
  • Loading branch information
KristofferC authored and mkitti committed Apr 13, 2024
1 parent 15bbfd5 commit 20cd39b
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 2 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ Compiler/Runtime improvements
Command-line option changes
---------------------------

* The `-m/--module` flag can be passed to run the `main` function inside a package with a set of arguments.
This `main` function should be declared using `@main` to indicate that it is an entry point.

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 (use `@main` to do so)")
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 @@ -106,6 +106,11 @@ Enable or disable usage of native code caching in the form of pkgimages
-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 @@ -128,6 +128,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 @@ -271,7 +273,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 @@ -284,6 +286,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 @@ -421,6 +424,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 @@ -434,6 +438,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 @@ -886,6 +894,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 @@ -1545,3 +1545,8 @@ end
@test_throws SystemError("opening file $(repr(file))") include(file)
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 20cd39b

Please sign in to comment.