From ffb3458eaf645c33d4a47ec7573cca3bd1f0d43f Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Mon, 23 Nov 2015 19:49:25 -0600 Subject: [PATCH] Add support for determining name of script #14109 Note that PROGRAM_FILE was chosen as the name for this variable as it matches the help for the Julia CLI. --- NEWS.md | 3 +++ base/client.jl | 15 +++++++------- base/docs/helpdb/Base.jl | 5 +++-- base/exports.jl | 1 + base/initdefs.jl | 1 + doc/manual/getting-started.rst | 15 ++++++++------ doc/stdlib/constants.rst | 5 +++++ doc/stdlib/file.rst | 2 +- test/cmdlineargs.jl | 37 ++++++++++++++++++++++++++++------ 9 files changed, 61 insertions(+), 23 deletions(-) diff --git a/NEWS.md b/NEWS.md index e051e3b4a860c..ea481644e94ec 100644 --- a/NEWS.md +++ b/NEWS.md @@ -173,6 +173,8 @@ New language features * `@__LINE__` special macro now available to reflect invocation source line number ([#12727]). + * `PROGRAM_FILE` global is now available for determining the name of the running script ([#14114]). + Language changes ---------------- @@ -1768,3 +1770,4 @@ Too numerous to mention. [#14413]: https://github.com/JuliaLang/julia/issues/14413 [#14424]: https://github.com/JuliaLang/julia/issues/14424 [#14759]: https://github.com/JuliaLang/julia/issues/14759 +[#14114]: https://github.com/JuliaLang/julia/issues/14114 diff --git a/base/client.jl b/base/client.jl index 7864124718b14..fb9e48cf70cd6 100644 --- a/base/client.jl +++ b/base/client.jl @@ -177,10 +177,9 @@ end # try to include() a file, ignoring if not found try_include(path::AbstractString) = isfile(path) && include(path) -function process_options(opts::JLOptions, args::Vector{UTF8String}) - if !isempty(args) - arg = first(args) - idxs = find(x -> x == "--", args) +function process_options(opts::JLOptions) + if !isempty(ARGS) + idxs = find(x -> x == "--", ARGS) if length(idxs) > 1 println(STDERR, "julia: redundant option terminator `--`") exit(1) @@ -234,15 +233,15 @@ function process_options(opts::JLOptions, args::Vector{UTF8String}) eval(Main, parse_input_line(bytestring(opts.postboot))) end # load file - if !isempty(args) && !isempty(args[1]) + if !isempty(ARGS) && !isempty(ARGS[1]) # program repl = false # remove filename from ARGS - shift!(ARGS) + global PROGRAM_FILE = UTF8String(shift!(ARGS)) if !is_interactive ccall(:jl_exit_on_sigint, Void, (Cint,), 1) end - include(args[1]) + include(PROGRAM_FILE) end break end @@ -298,7 +297,7 @@ function _start() append!(ARGS, Core.ARGS) opts = JLOptions() try - (quiet,repl,startup,color_set,history_file) = process_options(opts,copy(ARGS)) + (quiet,repl,startup,color_set,history_file) = process_options(opts) local term global active_repl diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl index add5299ce304a..41a1126851e7a 100644 --- a/base/docs/helpdb/Base.jl +++ b/base/docs/helpdb/Base.jl @@ -8368,8 +8368,9 @@ graphemes """ @__FILE__ -> AbstractString -`@__FILE__` expands to a string with the absolute path and file name of the script being -run. Returns `nothing` if run from a REPL or an empty string if evaluated by `julia -e `. +`@__FILE__` expands to a string with the absolute file path of the file containing the +macro. Returns `nothing` if run from a REPL or an empty string if evaluated by +`julia -e `. Alternatively see [`PROGRAM_FILE`](:data:`PROGRAM_FILE`). """ :@__FILE__ diff --git a/base/exports.jl b/base/exports.jl index 3b322a9d71914..8e5f197cb47ae 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -180,6 +180,7 @@ export JULIA_HOME, LOAD_PATH, OS_NAME, + PROGRAM_FILE, STDERR, STDIN, STDOUT, diff --git a/base/initdefs.jl b/base/initdefs.jl index cfd5670032eac..9c0e093fe9e2b 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -2,6 +2,7 @@ ## initdefs.jl - initialization and runtime management definitions +PROGRAM_FILE = UTF8String("") const ARGS = UTF8String[] exit(n) = ccall(:jl_exit, Void, (Int32,), n) diff --git a/doc/manual/getting-started.rst b/doc/manual/getting-started.rst index 8c43878d5c1d9..37ee881c2ca12 100644 --- a/doc/manual/getting-started.rst +++ b/doc/manual/getting-started.rst @@ -51,19 +51,22 @@ argument to the julia command:: As the example implies, the following command-line arguments to julia are taken as command-line arguments to the program ``script.jl``, passed -in the global constant ``ARGS``. ``ARGS`` is also set when script code -is given using the ``-e`` option on the command line (see the ``julia`` -help output below). For example, to just print the arguments given to a -script, you could do this:: +in the global constant ``ARGS``. The name of the script itself is passed +in as the global ``PROGRAM_FILE``. Note that ``ARGS`` is also set when script +code is given using the ``-e`` option on the command line (see the ``julia`` +help output below) but ``PROGRAM_FILE`` will be empty. For example, to just +print the arguments given to a script, you could do this:: + + $ julia -e 'println(PROGRAM_FILE); for x in ARGS; println(x); end' foo bar - $ julia -e 'for x in ARGS; println(x); end' foo bar foo bar Or you could put that code into a script and run it:: - $ echo 'for x in ARGS; println(x); end' > script.jl + $ echo 'println(PROGRAM_FILE); for x in ARGS; println(x); end' > script.jl $ julia script.jl foo bar + script.jl foo bar diff --git a/doc/stdlib/constants.rst b/doc/stdlib/constants.rst index 2577ce6fbb5ca..2ee60422d030a 100644 --- a/doc/stdlib/constants.rst +++ b/doc/stdlib/constants.rst @@ -14,6 +14,11 @@ Constants A symbol representing the name of the operating system. Possible values are ``:Linux``, ``:Darwin`` (OS X), or ``:Windows``. +.. data:: PROGRAM_FILE + + A string containing the script name passed to Julia from the command line. Note that the + script name remains unchanged from within included files. Alternatively see :data:`@__FILE__`. + .. data:: ARGS An array of the command line arguments passed to Julia, as strings. diff --git a/doc/stdlib/file.rst b/doc/stdlib/file.rst index fb86800822797..51501c5867d26 100644 --- a/doc/stdlib/file.rst +++ b/doc/stdlib/file.rst @@ -337,7 +337,7 @@ .. Docstring generated from Julia source - ``@__FILE__`` expands to a string with the absolute path and file name of the script being run. Returns ``nothing`` if run from a REPL or an empty string if evaluated by ``julia -e ``\ . + ``@__FILE__`` expands to a string with the absolute file path of the file containing the macro. Returns ``nothing`` if run from a REPL or an empty string if evaluated by ``julia -e ``. Alternatively see :data:`PROGRAM_FILE`. .. function:: @__LINE__ -> Int diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index b0c78d2b9c821..63b8d7329c100 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -204,24 +204,49 @@ let exename = `$(joinpath(JULIA_HOME, Base.julia_exename())) --precompiled=yes` # --worker takes default / custom as arugment (default/custom arguments tested in test/parallel.jl, test/examples.jl) @test !success(`$exename --worker=true`) + escape(str) = replace(str, "\\", "\\\\") + # test passing arguments let testfile = tempname() try - # write a julia source file that just prints ARGS to STDOUT and exits + # write a julia source file that just prints ARGS to STDOUT write(testfile, """ println(ARGS) - exit(0) """) - @test readchomp(`$exename $testfile foo -bar --baz`) == "UTF8String[\"foo\",\"-bar\",\"--baz\"]" - @test readchomp(`$exename $testfile -- foo -bar --baz`) == "UTF8String[\"foo\",\"-bar\",\"--baz\"]" - @test readchomp(`$exename -L $testfile -- foo -bar --baz`) == "UTF8String[\"foo\",\"-bar\",\"--baz\"]" + @test readchomp(`$exename $testfile foo -bar --baz`) == "UTF8String[\"foo\",\"-bar\",\"--baz\"]" + @test readchomp(`$exename $testfile -- foo -bar --baz`) == "UTF8String[\"foo\",\"-bar\",\"--baz\"]" + @test readchomp(`$exename -L $testfile -e 'exit(0)' -- foo -bar --baz`) == "UTF8String[\"foo\",\"-bar\",\"--baz\"]" + @test split(readchomp(`$exename -L $testfile $testfile`), '\n') == ["UTF8String[\"$(escape(testfile))\"]", "UTF8String[]"] @test !success(`$exename --foo $testfile`) - @test !success(`$exename -L $testfile -- foo -bar -- baz`) + @test !success(`$exename -L $testfile -e 'exit(0)' -- foo -bar -- baz`) finally rm(testfile) end end + # test the script name + let a = tempname(), b = tempname() + try + write(a, """ + println(@__FILE__) + println(PROGRAM_FILE) + println(length(ARGS)) + include(\"$(escape(b))\") + """) + write(b, """ + println(@__FILE__) + println(PROGRAM_FILE) + println(length(ARGS)) + """) + @test split(readchomp(`$exename $a`), '\n') == ["$a", "$a", "0", "$b", "$a", "0"] + @test split(readchomp(`$exename -L $b -e 'exit(0)'`), '\n') == ["$(realpath(b))", "", "0"] + @test split(readchomp(`$exename -L $b $a`), '\n') == ["$(realpath(b))", "", "1", "$a", "$a", "0", "$b", "$a", "0"] + finally + rm(a) + rm(b) + end + end + # issue #10562 @test readchomp(`$exename -e 'println(ARGS);' ''`) == "UTF8String[\"\"]"