Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 92 additions & 13 deletions base/initdefs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ function init_depot_path()
end
end

## LOAD_PATH ##
## LOAD_PATH, HOME_PROJECT & ACTIVE_PROJECT ##

# split on `:` (or `;` on Windows)
# JULIA_LOAD_PATH: split on `:` (or `;` on Windows)
# first empty entry is replaced with DEFAULT_LOAD_PATH, the rest are skipped
# entries starting with `@` are named environments:
# - the first three `#`s in a named environment are replaced with version numbers
Expand All @@ -70,7 +70,7 @@ end
# this will inherit an existing JULIA_LOAD_PATH value or if there is none, leave
# a trailing empty entry in JULIA_LOAD_PATH which will be replaced with defaults.

const DEFAULT_LOAD_PATH = ["@@", "@v#.#", "@stdlib"]
const DEFAULT_LOAD_PATH = ["@", "@v#.#", "@stdlib"]

"""
LOAD_PATH
Expand All @@ -79,8 +79,10 @@ An array of paths for `using` and `import` statements to consdier as project
environments or package directories when loading code. See Code Loading.
"""
const LOAD_PATH = copy(DEFAULT_LOAD_PATH)
const HOME_PROJECT = Ref{Union{String,Nothing}}(nothing)
const ACTIVE_PROJECT = Ref{Union{String,Nothing}}(nothing)

function current_env(dir::AbstractString)
function current_project(dir::AbstractString)
# look for project file in current dir and parents
home = homedir()
while true
Expand All @@ -95,13 +97,13 @@ function current_env(dir::AbstractString)
end
end

function current_env()
function current_project()
dir = try pwd()
catch err
err isa UVError || rethrow(err)
return nothing
end
return current_env(dir)
return current_project(dir)
end

function parse_load_path(str::String)
Expand All @@ -112,8 +114,8 @@ function parse_load_path(str::String)
if isempty(env)
first_empty && append!(envs, DEFAULT_LOAD_PATH)
first_empty = false
elseif env == "@" # use "@@" to do delayed expansion
dir = current_env()
elseif env == "@."
dir = current_project()
dir !== nothing && push!(envs, dir)
else
push!(envs, env)
Expand All @@ -124,14 +126,91 @@ end

function init_load_path()
if Base.creating_sysimg
load_path = ["@stdlib"]
paths = ["@stdlib"]
elseif haskey(ENV, "JULIA_LOAD_PATH")
load_path = parse_load_path(ENV["JULIA_LOAD_PATH"])
paths = parse_load_path(ENV["JULIA_LOAD_PATH"])
else
load_path = filter!(env -> env !== nothing,
[env == "@" ? current_env() : env for env in DEFAULT_LOAD_PATH])
paths = filter!(env -> env !== nothing,
[env == "@." ? current_project() : env for env in DEFAULT_LOAD_PATH])
end
append!(empty!(LOAD_PATH), load_path)
project = (JLOptions().project != C_NULL ?
unsafe_string(Base.JLOptions().project) :
get(ENV, "JULIA_PROJECT", nothing))
HOME_PROJECT[] =
project == "" ? nothing :
project == "@." ? current_project() : project
append!(empty!(LOAD_PATH), paths)
end

## load path expansion: turn LOAD_PATH entries into concrete paths ##

function load_path_expand(env::AbstractString)::Union{String, Nothing}
# named environment?
if startswith(env, '@')
# `@` in JULIA_LOAD_PATH is expanded early (at startup time)
# if you put a `@` in LOAD_PATH manually, it's expanded late
env == "@" && return active_project(false)
env == "@." && return current_project()
env == "@stdlib" && return Sys.STDLIB
env = replace(env, '#' => VERSION.major, count=1)
env = replace(env, '#' => VERSION.minor, count=1)
env = replace(env, '#' => VERSION.patch, count=1)
name = env[2:end]
# look for named env in each depot
for depot in DEPOT_PATH
path = joinpath(depot, "environments", name)
isdir(path) || continue
for proj in project_names
file = abspath(path, proj)
isfile_casesensitive(file) && return file
end
return path
end
isempty(DEPOT_PATH) && return nothing
return abspath(DEPOT_PATH[1], "environments", name, project_names[end])
end
# otherwise, it's a path
path = abspath(env)
if isdir(path)
# directory with a project file?
for proj in project_names
file = joinpath(path, proj)
isfile_casesensitive(file) && return file
end
end
# package dir or path to project file
return path
end
load_path_expand(::Nothing) = nothing

function active_project(search_load_path::Bool=true)
for project in (ACTIVE_PROJECT[], HOME_PROJECT[])
project == "@" && continue
project = load_path_expand(project)
project === nothing && continue
if !isfile_casesensitive(project)
project = abspath(project, "Project.toml")
end
return project
end
search_load_path || return
for project in LOAD_PATH
project == "@" && continue
project = load_path_expand(project)
project === nothing && continue
isfile_casesensitive(project) && return project
ispath(project) && continue
basename(project) in project_names && return project
end
end

function load_path()
paths = String[]
for env in LOAD_PATH
path = load_path_expand(env)
path !== nothing && path ∉ paths && push!(paths, path)
end
return paths
end

## atexit: register exit hooks ##
Expand Down
41 changes: 0 additions & 41 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -145,47 +145,6 @@ function version_slug(uuid::UUID, sha1::SHA1, p::Int=4)
return slug(crc, p)
end

## load path expansion: turn LOAD_PATH entries into concrete paths ##

function load_path_expand(env::AbstractString)::Union{String, Nothing}
# named environment?
if startswith(env, '@')
# `@` in JULIA_LOAD_PATH is expanded early (at startup time)
# if you put a `@` in LOAD_PATH manually, it's expanded late
(env == "@" || env == "@@") && return current_env()
env == "@stdlib" && return Sys.STDLIB
env = replace(env, '#' => VERSION.major, count=1)
env = replace(env, '#' => VERSION.minor, count=1)
env = replace(env, '#' => VERSION.patch, count=1)
name = env[2:end]
# look for named env in each depot
for depot in DEPOT_PATH
path = joinpath(depot, "environments", name)
isdir(path) || continue
for proj in project_names
file = abspath(path, proj)
isfile_casesensitive(file) && return file
end
return path
end
isempty(DEPOT_PATH) && return nothing
return abspath(DEPOT_PATH[1], "environments", name, project_names[end])
end
# otherwise, it's a path
path = abspath(env)
if isdir(path)
# directory with a project file?
for proj in project_names
file = joinpath(path, proj)
isfile_casesensitive(file) && return file
end
end
# package dir or path to project file
return path
end

load_path() = String[env for env in map(load_path_expand, LOAD_PATH) if env ≠ nothing]

## package identification: determine unique identity of package to be loaded ##

find_package(args...) = locate_package(identify_package(args...))
Expand Down
1 change: 1 addition & 0 deletions base/options.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct JLOptions
cpu_target::Ptr{UInt8}
nprocs::Int32
machine_file::Ptr{UInt8}
project::Ptr{UInt8}
isinteractive::Int8
color::Int8
historyfile::Int8
Expand Down
6 changes: 6 additions & 0 deletions src/jloptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jl_options_t jl_options = { 0, // quiet
-1, // banner
NULL, // julia_bindir
NULL, // julia_bin
NULL, // project
NULL, // cmds
NULL, // image_file (will be filled in below)
NULL, // cpu_target ("native", "core2", etc...)
Expand Down Expand Up @@ -171,6 +172,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
opt_sysimage_native_code,
opt_compiled_modules,
opt_machine_file,
opt_project,
};
static const char* const shortopts = "+vhqH:e:E:L:J:C:ip:O:g:";
static const struct option longopts[] = {
Expand All @@ -194,6 +196,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
{ "procs", required_argument, 0, 'p' },
{ "machinefile", required_argument, 0, opt_machinefile }, // deprecated
{ "machine-file", required_argument, 0, opt_machine_file },
{ "project", optional_argument, 0, opt_project },
{ "color", required_argument, 0, opt_color },
{ "history-file", required_argument, 0, opt_history_file },
{ "startup-file", required_argument, 0, opt_startup_file },
Expand Down Expand Up @@ -399,6 +402,9 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
if (!jl_options.machine_file)
jl_error("julia: failed to allocate memory");
break;
case opt_project:
jl_options.project = optarg ? strdup(optarg) : "@.";
break;
case opt_color:
if (!strcmp(optarg, "yes"))
jl_options.color = JL_OPTIONS_COLOR_ON;
Expand Down
1 change: 1 addition & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -1778,6 +1778,7 @@ typedef struct {
const char *cpu_target;
int32_t nprocs;
const char *machine_file;
const char *project;
int8_t isinteractive;
int8_t color;
int8_t historyfile;
Expand Down
4 changes: 2 additions & 2 deletions stdlib/Pkg/docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ end # module
We can now activate the project and load the package:

```jl
pkg> activate
pkg> activate .

julia> import HelloWorld

Expand Down Expand Up @@ -773,7 +773,7 @@ However, nothing would be installed and your `Project.toml` and `Manifest.toml`
Simply clone their project using e.g. `git clone`, `cd` to the project directory and call

```
(v0.7) pkg> activate
(v0.7) pkg> activate .

(SomeProject) pkg> instantiate
```
Expand Down
27 changes: 2 additions & 25 deletions stdlib/Pkg/src/API.jl
Original file line number Diff line number Diff line change
Expand Up @@ -548,31 +548,8 @@ function instantiate(ctx::Context; manifest::Union{Bool, Nothing}=nothing, kwarg
Operations.build_versions(ctx, union(new_apply, new_git))
end

const ACTIVE_ENV = Ref{Union{String,Nothing}}(nothing)

function _activate(env::Union{String,Nothing})
if env === nothing
@warn "Current directory is not in a project, nothing activated."
else
if !isempty(LOAD_PATH) && ACTIVE_ENV[] === LOAD_PATH[1]
LOAD_PATH[1] = env
else
# TODO: warn if ACTIVE_ENV !== nothing ?
pushfirst!(LOAD_PATH, env)
end
ACTIVE_ENV[] = env
end
end
activate() = _activate(Base.current_env())
activate(path::String) = _activate(Base.current_env(path))

function deactivate()
if !isempty(LOAD_PATH) && ACTIVE_ENV[] === LOAD_PATH[1]
popfirst!(LOAD_PATH)
else
# warn if ACTIVE_ENV !== nothing ?
end
ACTIVE_ENV[] = nothing
function activate(path::Union{String,Nothing}=nothing)
Base.ACTIVE_PROJECT[] = Base.load_path_expand(path)
end

end # module
1 change: 0 additions & 1 deletion stdlib/Pkg/src/Pkg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ const resolve = API.resolve
const status = Display.status
const update = up
const activate = API.activate
const deactivate = API.deactivate

# legacy CI script support
import .API: clone, dir
Expand Down
30 changes: 7 additions & 23 deletions stdlib/Pkg/src/REPLMode.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ const cmds = Dict(
"instantiate" => CMD_INSTANTIATE,
"resolve" => CMD_RESOLVE,
"activate" => CMD_ACTIVATE,
"deactivate" => CMD_DEACTIVATE,
)

#################
Expand Down Expand Up @@ -279,7 +278,6 @@ function do_cmd!(tokens::Vector{Token}, repl)
cmd.kind == CMD_PRECOMPILE ? Base.invokelatest( do_precompile!, ctx, tokens) :
cmd.kind == CMD_INSTANTIATE ? Base.invokelatest( do_instantiate!, ctx, tokens) :
cmd.kind == CMD_ACTIVATE ? Base.invokelatest( do_activate!, ctx, tokens) :
cmd.kind == CMD_DEACTIVATE ? Base.invokelatest( do_deactivate!, ctx, tokens) :
cmderror("`$cmd` command not yet implemented")
return
end
Expand Down Expand Up @@ -345,8 +343,6 @@ developed packages
`gc`: garbage collect packages not used for a significant time

`activate`: set the primary environment the package manager manipulates

`deactivate`: unset the primary environment the package manager manipulates
"""

const helps = Dict(
Expand Down Expand Up @@ -794,11 +790,6 @@ function do_activate!(ctx::Context, tokens::Vector{Token})
end
end

function do_deactivate!(ctx::Context, tokens::Vector{Token})
!isempty(tokens) && cmderror("`deactivate` does not take any arguments")
API.deactivate()
end

######################
# REPL mode creation #
######################
Expand Down Expand Up @@ -936,22 +927,15 @@ function completions(full, index)
end

function promptf()
env = try
EnvCache()
project_file = Base.active_project()
try
env = EnvCache(project_file)
name = (env.pkg != nothing && !isempty(env.pkg.name) ?
env.pkg.name : basename(dirname(project_file)))
return "($name) pkg> "
catch
nothing
end
prefix = ""
if env !== nothing
proj_dir = dirname(env.project_file)
if startswith(pwd(), proj_dir) && env.pkg != nothing && !isempty(env.pkg.name)
name = env.pkg.name
else
name = basename(proj_dir)
end
prefix = string("(", name, ") ")
end
return prefix * "pkg> "
return "pkg> "
end

# Set up the repl Pkg REPLMode
Expand Down
Loading