Skip to content

Commit

Permalink
Stop trying to be lazy (#527)
Browse files Browse the repository at this point in the history
* just use TOML for Registry.toml and set up a cache

* prevent a O(n^2) when collecting package names

* Update Types.jl
  • Loading branch information
KristofferC committed Jul 27, 2018
1 parent ce60e9c commit ea35e10
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 112 deletions.
2 changes: 1 addition & 1 deletion stdlib/Pkg/src/REPLMode.jl
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,7 @@ function complete_remote_package(s, i1, i2)
cmp = String[]
julia_version = VERSION
for reg in Types.registries(;clone_default=false)
data = TOML.parsefile(joinpath(reg, "Registry.toml"))
data = Types.read_registry(joinpath(reg, "Registry.toml"))
for (uuid, pkginfo) in data["packages"]
name = pkginfo["name"]
if startswith(name, s)
Expand Down
201 changes: 90 additions & 111 deletions stdlib/Pkg/src/Types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,29 @@ end # trynames
global manifestfile_path = trynames(Base.manifest_names)
end # let

function find_project_file(env::Union{Nothing,String}=nothing)
project_file = nothing
if env isa Nothing
project_file = Base.active_project()
project_file == nothing && error("no active project")
elseif startswith(env, '@')
project_file = Base.load_path_expand(env)
project_file === nothing && error("package environment does not exist: $env")
elseif env isa String
if isdir(env)
isempty(readdir(env)) || error("environment is a package directory: $env")
project_file = joinpath(env, Base.project_names[end])
else
project_file = endswith(env, ".toml") ? abspath(env) :
abspath(env, Base.project_names[end])
end
end
@assert project_file isa String &&
(isfile(project_file) || !ispath(project_file) ||
isdir(project_file) && isempty(readdir(project_file)))
return project_file
end

mutable struct EnvCache
# environment info:
env::Union{Nothing,String}
Expand All @@ -235,60 +258,46 @@ mutable struct EnvCache
# registered package info:
uuids::Dict{String,Vector{UUID}}
paths::Dict{UUID,Vector{String}}
names::Dict{UUID,Vector{String}}
end

function EnvCache(env::Union{Nothing,String}=nothing)
if env isa Nothing
project_file = Base.active_project()
project_file == nothing && error("no active project")
elseif startswith(env, '@')
project_file = Base.load_path_expand(env)
project_file === nothing && error("package environment does not exist: $env")
elseif env isa String
if isdir(env)
isempty(readdir(env)) || error("environment is a package directory: $env")
project_file = joinpath(env, Base.project_names[end])
else
project_file = endswith(env, ".toml") ? abspath(env) :
abspath(env, Base.project_names[end])
end
end
@assert project_file isa String &&
(isfile(project_file) || !ispath(project_file) ||
isdir(project_file) && isempty(readdir(project_file)))
project_dir = dirname(project_file)
git = ispath(joinpath(project_dir, ".git")) ? project_dir : nothing

project = read_project(project_file)
if any(haskey.((project,), ["name", "uuid", "version"]))
project_package = PackageSpec(
get(project, "name", ""),
UUID(get(project, "uuid", 0)),
VersionNumber(get(project, "version", "0.0")),
)
else
project_package = nothing
end
# determine manifest_file name
dir = abspath(dirname(project_file))
manifest_file = haskey(project, "manifest") ?
abspath(project["manifest"]) :
manifestfile_path(dir)
# use default name if still not determined
(manifest_file === nothing) && (manifest_file = joinpath(dir, "Manifest.toml"))
write_env_usage(manifest_file)
manifest = read_manifest(manifest_file)
uuids = Dict{String,Vector{UUID}}()
paths = Dict{UUID,Vector{String}}()
return new(env,
git,
project_file,
manifest_file,
project_package,
project,
manifest,
uuids,
paths,)
function EnvCache(env::Union{Nothing,String}=nothing)
project_file = find_project_file(env)
project_dir = dirname(project_file)
git = ispath(joinpath(project_dir, ".git")) ? project_dir : nothing

project = read_project(project_file)
if any(haskey.((project,), ["name", "uuid", "version"]))
project_package = PackageSpec(
get(project, "name", ""),
UUID(get(project, "uuid", 0)),
VersionNumber(get(project, "version", "0.0")),
)
else
project_package = nothing
end
# determine manifest_file name
dir = abspath(dirname(project_file))
manifest_file = haskey(project, "manifest") ?
abspath(project["manifest"]) :
manifestfile_path(dir)
# use default name if still not determined
(manifest_file === nothing) && (manifest_file = joinpath(dir, "Manifest.toml"))
write_env_usage(manifest_file)
manifest = read_manifest(manifest_file)
uuids = Dict{String,Vector{UUID}}()
paths = Dict{UUID,Vector{String}}()
names = Dict{UUID,Vector{String}}()
return EnvCache(env,
git,
project_file,
manifest_file,
project_package,
project,
manifest,
uuids,
paths,
names,)
end

collides_with_project(env::EnvCache, pkg::PackageSpec) =
Expand Down Expand Up @@ -845,14 +854,20 @@ function registries(; clone_default=true)::Vector{String}
return [r for d in depots() for r in registries(d)]
end

const line_re = r"""
^ \s*
([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})
\s* = \s* \{
\s* name \s* = \s* "([^"]*)" \s*,
\s* path \s* = \s* "([^"]*)" \s*,?
\s* \} \s* $
"""x
# path -> (mtime, TOML Dict)
const REGISTRY_CACHE = Dict{String, Tuple{Float64, Dict{String, Any}}}()

function read_registry(reg_file)
t = mtime(reg_file)
if haskey(REGISTRY_CACHE, reg_file)
prev_t, registry = REGISTRY_CACHE[reg_file]
t == prev_t && return registry
end
registry = TOML.parsefile(reg_file)
REGISTRY_CACHE[reg_file] = (t, registry)
return registry
end


# Lookup package names & uuids in a single pass through registries
function find_registered!(env::EnvCache,
Expand Down Expand Up @@ -886,62 +901,26 @@ function find_registered!(env::EnvCache,
end
# if there's still nothing to look for, return early
isempty(names) && isempty(uuids) && return

# build regexs for names and uuids
uuid_re = sprint() do io
if !isempty(uuids)
print(io, raw"^( ")
for (i, uuid) in enumerate(uuids)
1 < i && print(io, " | ")
print(io, raw"\Q", uuid, raw"\E")
end
print(io, raw" )\b")
end
end
name_re = sprint() do io
if !isempty(names)
print(io, raw"\bname \s* = \s* \"( ")
for (i, name) in enumerate(names)
1 < i && print(io, " | ")
print(io, raw"\Q", name, raw"\E")
end
print(io, raw" )\"")
end
end
regex = if !isempty(uuids) && !isempty(names)
Regex("( $uuid_re | $name_re )", "x")
elseif !isempty(uuids)
Regex(uuid_re, "x")
elseif !isempty(names)
Regex(name_re, "x")
else
error("this should not happen")
end

# initialize env entries for names and uuids
for name in names; env.uuids[name] = UUID[]; end
for uuid in uuids; env.paths[uuid] = String[]; end
# note: empty vectors will be left for names & uuids that aren't found
for uuid in uuids; env.names[uuid] = String[]; end

# search through all registries
# note: empty vectors will be left for names & uuids that aren't found
for registry in registries()
open(joinpath(registry, "Registry.toml")) do io
# skip forward until [packages] section
for line in eachline(io)
occursin(r"^ \s* \[ \s* packages \s* \] \s* $"x, line) && break
end
# find lines with uuid or name we're looking for
for line in eachline(io)
occursin(regex, line) || continue
m = match(line_re, line)
m == nothing &&
error("misformatted registry.toml package entry: $line")
uuid = UUID(m.captures[1])
name = Base.unescape_string(m.captures[2])
path = abspath(registry, Base.unescape_string(m.captures[3]))
push!(get!(env.uuids, name, typeof(uuid)[]), uuid)
push!(get!(env.paths, uuid, typeof(path)[]), path)
data = read_registry(joinpath(registry, "Registry.toml"))
for (_uuid, pkgdata) in data["packages"]
uuid = UUID(_uuid)
name = pkgdata["name"]
path = abspath(registry, pkgdata["path"])
push!(get!(env.uuids, name, UUID[]), uuid)
push!(get!(env.paths, uuid, String[]), path)
push!(get!(env.names, uuid, String[]), name)
end
end
for d in (env.uuids, env.paths, env.names)
for (k, v) in d
unique!(v)
end
end
end
Expand All @@ -968,7 +947,7 @@ end
#Get registered names associated with a package uuid
function registered_names(env::EnvCache, uuid::UUID)::Vector{String}
find_registered!(env, String[], [uuid])
String[n for (n, uuids) in env.uuids for u in uuids if u == uuid]
return env.names[uuid]
end

# Determine a single UUID for a given name, prompting if needed
Expand Down

0 comments on commit ea35e10

Please sign in to comment.