When starting Julia in the command line one can specify project directory. One of the options is @.
, resumably the current directory. What is @.
in this context?
# from `julia --help`
--project[={<dir>|@.}] Set <dir> as the home project/environment
From the docs:
If the variable is set to @., Julia tries to find a project directory that contains Project.toml or JuliaProject.toml file from the current directory and its parents.
I realise the cli --project
argument parsing occurs in this C code, and apparently used by this init code, although I'm not sure of the sequence of events in between.
Specifically in initdefs.jl we have:
project = (JLOptions().project != C_NULL ?
unsafe_string(Base.JLOptions().project) :
get(ENV, "JULIA_PROJECT", nothing))
HOME_PROJECT[] =
project == nothing ? nothing :
project == "" ? nothing :
project == "@." ? current_project() : abspath(project)
My reading of this code is that @.
is just an arbitrary token, could it have been a simple .
for cli? How does Julia search "the current directory and its parents."
There is similar notation used in LOAD_PATH
(["@", "@v#.#", "@stdlib"]
),
as discussed here and here. Does @.
belong to the same family of expansion as LOAD_PATH
symbols?
--project
The --project
flag defines the "home project" or "home environment". An environment is defined by a Project.toml
/Manifest.toml
and defines which package are available for using
/import
.
You can set --project
to (i) a directory (with or without a Project.toml
) (ii) a path to a Project.toml
or (iii) to @.
. (i) and (ii) are pretty self-explanatory -- Julia will consider the project located at the path to be the home project. Now for (iii), if you set --project=@.
Julia will try to find an existing Project.toml
file and use that as the home project. Consider the following example:
~$ tree .
.
├── A
└── Project.toml
1 directory, 1 file
where A
is an empty directory. We can try (i) and (ii) easily:
# existing file
[~]$ julia --project=Project.toml -E 'Base.active_project()'
"~/Project.toml"
# existing directory
[~]$ julia --project=A -E 'Base.active_project()'
"~/A/Project.toml"
# non-existing directory
[~]$ julia --project=B -E 'Base.active_project()'
"~/B/Project.toml"
# non-existing file
[~]$ julia --project=B/Project.toml -E 'Base.active_project()'
"~/B/Project.toml"
Note that in the three last examples above the Project.toml
file do not exist, but it will be created if needed (e.g. when using Pkg
to manipulate packages).
Now, compare this with the behaviour of @.
, which will look for an existing project file:
# from our root directory
[~]$ julia --project=@. -E 'Base.active_project()'
"~/Project.toml"
# from inside the A directory
[~/A]$ julia --project=@. -E 'Base.active_project()'
"~/Project.toml"
where in both cases we found the same Project.toml
file. With the @.
option Julia first look in the current directory for a Project.toml
file, and if not found, steps up one level to the parent folder and looks there, and so on. This is what happened in the second example; Julia did not find a Project.toml
file in the empty A
directory, so we moved up to the parent directory, and found the Project.toml
we have there.
And yes, we could have choosen some other token for this, but not .
since that already has a meaning; it is the path to the current directory, and a perfectly valid path to use with --project
.
"@"
To load a package Example
it is not enough that we define a home project with Example
in its [deps]
section; the home project also needs to show up in Julias load path (Base.load_path()
). By default the load path is expanded from ["@", "@v#.#", "@stdlib"]
where "@v#.#"
expands to ~/.julia/environments/v#.#
with #
replaced with Julias major and minor version number, and "@stdlib"
expands to the directory with Julias standard library. "@"
expands to 1. an active project (activated with Pkg.activate
/pkg> activate
) or 2. the home project. We can check the expanded load path with Base.load_path()
:
# without home project; @ expands to nothing
[~]$ julia -E 'Base.load_path()'
["~/.julia/environments/v1.0/Project.toml", "~/julia10/usr/share/julia/stdlib/v1.0"]
# with home project; @ expands to our specified home project
[~]$ julia --project=@. -E 'Base.load_path()'
["~/Project.toml", "~/.julia/environments/v1.0/Project.toml", "~/julia10/usr/share/julia/stdlib/v1.0"]
And finally, if we remove "@"
from the load path it does not matter that we have defined a home project:
[~]$ export JULIA_LOAD_PATH="" && julia --project=@. -E 'Base.load_path()'
String[]
Based on @fredrikekre answer, wrote a script to explore behaviour of Base.load_path()
, Base.active_project()
, Base.current_project()
.
init_me.jl:
"""
Explore Base.load_path() behaviour in diffferent situations.
Run this file as:
julia init_me.jl
julia --project=@. init_me.jl
julia --project=. init_me.jl
Try running in a folder that has or does not have Project.toml file.
"""
MESSAGE = Dict(true => " (exists)", false => " (does not exist)")
exist(path)::Bool = isfile(path) || isdir(path)
printexist(path::String) = println(" ", path, MESSAGE[exist(path)])
printexist(nothing) = println("nothing")
printf2(s,n=8) = print(" " * s * ' '^(n-length(s)))
p_ = Base.JLOptions().project
project_option = (p_ != C_NULL) ? unsafe_string(p_) : "option not provided"
println("--project:\n ", project_option)
println("load_path():")
for path in Base.load_path()
printexist(path)
end
println("Base.active_project():")
printexist(Base.active_project())
println("Base.current_project():")
printexist(Base.current_project())
println("alias expansion with Base.load_path_expand():")
for alias in ["@.", "@", "@stdlib", "@v1.0"]
printf2(alias)
printexist(Base.load_path_expand(alias))
end
The results are as following:
julia --project=@. init_me.jl
in folder with Project.toml file produces most expected resultjulia --project=. init_me.jl
fails on folders where path has non-ASCII charactersProject.toml
can become an active project upon --project=<some dir without Project.toml>
Base.active_project()
is not Base.current_project()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With