Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what is `@.` in Julia `--project` command line option?

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?

like image 762
Evgeny Avatar asked Dec 04 '18 13:12

Evgeny


2 Answers

Home project and --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.

The load path and "@"

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[]
like image 57
fredrikekre Avatar answered Oct 15 '22 15:10

fredrikekre


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:

  • basecase: julia --project=@. init_me.jl in folder with Project.toml file produces most expected result
  • julia --project=. init_me.jl fails on folders where path has non-ASCII characters
  • a folder without Project.toml can become an active project upon --project=<some dir without Project.toml>
  • sometimes Base.active_project() is not Base.current_project()
like image 25
Evgeny Avatar answered Oct 15 '22 16:10

Evgeny