Skip to content

Commit

Permalink
make loading work with a entryfile entry in the manifest file (#53939)
Browse files Browse the repository at this point in the history
also soft deprecate the `path` entry in project file in favour of using
`entryfile` instead

Fixes the Julia Base part of #53937
  • Loading branch information
KristofferC authored Apr 5, 2024
1 parent 6ea67a9 commit 5f4dec1
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 24 deletions.
41 changes: 24 additions & 17 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,6 @@ function locate_package_env(pkg::PkgId, stopenv::Union{String, Nothing}=nothing)
@goto done
end
if path !== nothing
path = entry_path(path, pkg.name)
env′ = env
@goto done
end
Expand All @@ -438,12 +437,15 @@ function locate_package_env(pkg::PkgId, stopenv::Union{String, Nothing}=nothing)
# e.g. if they have been explicitly added to the project/manifest
mbypath = manifest_uuid_path(Sys.STDLIB, pkg)
if mbypath isa String
path = entry_path(mbypath, pkg.name)
path = mbypath
env′ = Sys.STDLIB
@goto done
end
end
@label done
if path !== nothing && !isfile_casesensitive(path)
path = nothing
end
if cache !== nothing
cache.located[(pkg, stopenv)] = path === nothing ? nothing : (path, something(env′))
end
Expand Down Expand Up @@ -690,7 +692,7 @@ function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missi
proj = project_file_name_uuid(project_file, pkg.name)
if proj == pkg
# if `pkg` matches the project, return the project itself
return project_file_path(project_file)
return project_file_path(project_file, pkg.name)
end
mby_ext = project_file_ext_path(project_file, pkg.name)
mby_ext === nothing || return mby_ext
Expand Down Expand Up @@ -725,7 +727,7 @@ end

function project_file_ext_path(project_file::String, name::String)
d = parsed_toml(project_file)
p = project_file_path(project_file)
p = dirname(project_file)
exts = get(d, "extensions", nothing)::Union{Dict{String, Any}, Nothing}
if exts !== nothing
if name in keys(exts)
Expand All @@ -744,9 +746,14 @@ function project_file_name_uuid(project_file::String, name::String)::PkgId
return PkgId(uuid, name)
end

function project_file_path(project_file::String)
function project_file_path(project_file::String, name::String)
d = parsed_toml(project_file)
joinpath(dirname(project_file), get(d, "path", "")::String)
entryfile = get(d, "path", nothing)::Union{String, Nothing}
# "path" entry in project file is soft deprecated
if entryfile === nothing
entryfile = get(d, "entryfile", nothing)::Union{String, Nothing}
end
return entry_path(dirname(project_file), name, entryfile)
end

function workspace_manifest(project_file)
Expand Down Expand Up @@ -837,12 +844,11 @@ function implicit_env_project_file_extension(dir::String, ext::PkgId)
return nothing, nothing
end

# given a path and a name, return the entry point
function entry_path(path::String, name::String)::Union{Nothing,String}
# given a path, name, and possibly an entryfile, return the entry point
function entry_path(path::String, name::String, entryfile::Union{Nothing,String})::String
isfile_casesensitive(path) && return normpath(path)
path = normpath(joinpath(path, "src", "$name.jl"))
isfile_casesensitive(path) && return path
return nothing # source not found
entrypoint = entryfile === nothing ? joinpath("src", "$name.jl") : entryfile
return normpath(joinpath(path, entrypoint))
end

## explicit project & manifest API ##
Expand Down Expand Up @@ -1016,15 +1022,16 @@ end

function explicit_manifest_entry_path(manifest_file::String, pkg::PkgId, entry::Dict{String,Any})
path = get(entry, "path", nothing)::Union{Nothing, String}
entryfile = get(entry, "entryfile", nothing)::Union{Nothing, String}
if path !== nothing
path = normpath(abspath(dirname(manifest_file), path))
path = entry_path(normpath(abspath(dirname(manifest_file), path)), pkg.name, entryfile)
return path
end
hash = get(entry, "git-tree-sha1", nothing)::Union{Nothing, String}
if hash === nothing
mbypath = manifest_uuid_path(Sys.STDLIB, pkg)
if mbypath isa String
return entry_path(mbypath, pkg.name)
if mbypath isa String && isfile(mbypath)
return mbypath
end
return nothing
end
Expand All @@ -1034,7 +1041,7 @@ function explicit_manifest_entry_path(manifest_file::String, pkg::PkgId, entry::
for slug in (version_slug(uuid, hash), version_slug(uuid, hash, 4))
for depot in DEPOT_PATH
path = joinpath(depot, "packages", pkg.name, slug)
ispath(path) && return abspath(path)
ispath(path) && return entry_path(abspath(path), pkg.name, entryfile)
end
end
# no depot contains the package, return missing to stop looking
Expand Down Expand Up @@ -2504,8 +2511,8 @@ function require_stdlib(uuidkey::PkgId, ext::Union{Nothing, String}=nothing)
uuidkey = PkgId(uuid5(uuidkey.uuid, ext), ext)
end
#mbypath = manifest_uuid_path(env, uuidkey)
#if mbypath isa String
# sourcepath = entry_path(mbypath, uuidkey.name)
#if mbypath isa String && isfile_casesensitive(mbypath)
# sourcepath = mbypath
#else
# # if the user deleted the stdlib folder, we next try using their environment
# sourcepath = locate_package_env(uuidkey)
Expand Down
12 changes: 6 additions & 6 deletions doc/src/manual/code-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,12 @@ What happens if `import Zebra` is evaluated in the main `App` code base? Since `
**The paths map** of a project environment is extracted from the manifest file. The path of a package `uuid` named `X` is determined by these rules (in order):

1. If the project file in the directory matches `uuid` and name `X`, then either:
- It has a toplevel `path` entry, then `uuid` will be mapped to that path, interpreted relative to the directory containing the project file.
- Otherwise, `uuid` is mapped to `src/X.jl` relative to the directory containing the project file.
2. If the above is not the case and the project file has a corresponding manifest file and the manifest contains a stanza matching `uuid` then:
- If it has a `path` entry, use that path (relative to the directory containing the manifest file).
- If it has a `git-tree-sha1` entry, compute a deterministic hash function of `uuid` and `git-tree-sha1`—call it `slug`—and look for a directory named `packages/X/$slug` in each directory in the Julia `DEPOT_PATH` global array. Use the first such directory that exists.
- It has a toplevel `entryfile` entry, then `uuid` will be mapped to that path, interpreted relative to the directory containing the project file.
- Otherwise, `uuid` is mapped to `src/X.jl` relative to the directory containing the project file.
2. 1. If the above is not the case and the project file has a corresponding manifest file and the manifest contains a stanza matching `uuid` then:
- If it has a `path` entry, use that path (relative to the directory containing the manifest file).
- If it has a `git-tree-sha1` entry, compute a deterministic hash function of `uuid` and `git-tree-sha1`—call it `slug`—and look for a directory named `packages/X/$slug` in each directory in the Julia `DEPOT_PATH` global array. Use the first such directory that exists.
2. If this is a directory then `uuid` is mapped to `src/X.jl` unless the matching manifest stanza has an `entryfile` entry in which case this is used. In both cases, these are relative to the directory in 2.1.

If any of these result in success, the path to the source code entry point will be either that result, the relative path from that result plus `src/X.jl`; otherwise, there is no path mapping for `uuid`. When loading `X`, if no source code path is found, the lookup will fail, and the user may be prompted to install the appropriate package version or to take other corrective action (e.g. declaring `X` as a dependency).

Expand Down Expand Up @@ -208,7 +209,6 @@ This example map includes three different kinds of package locations (the first
2. The public `Priv` and `Zebra` packages are in the system depot, where packages installed and managed by the system administrator live. These are available to all users on the system.
3. The `Pub` package is in the user depot, where packages installed by the user live. These are only available to the user who installed them.


### Package directories

Package directories provide a simpler kind of environment without the ability to handle name collisions. In a package directory, the set of top-level packages is the set of subdirectories that "look like" packages. A package `X` exists in a package directory if the directory contains one of the following "entry point" files:
Expand Down
17 changes: 16 additions & 1 deletion test/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1609,5 +1609,20 @@ end

finally
copy!(LOAD_PATH, old_load_path)
end
end
end

@testset "project path handling" begin
old_load_path = copy(LOAD_PATH)
try
push!(LOAD_PATH, joinpath(@__DIR__, "project", "ProjectPath"))
id_project = Base.identify_package("ProjectPath")
Base.locate_package(id_project)
@test Base.locate_package(id_project) == joinpath(@__DIR__, "project", "ProjectPath", "CustomPath.jl")

id_dep = Base.identify_package("ProjectPathDep")
@test Base.locate_package(id_dep) == joinpath(@__DIR__, "project", "ProjectPath", "ProjectPathDep", "CustomPath.jl")
finally
copy!(LOAD_PATH, old_load_path)
end
end
5 changes: 5 additions & 0 deletions test/project/ProjectPath/CustomPath.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module ProjectPath

greet() = print("Hello World!")

end # module ProjectPath
18 changes: 18 additions & 0 deletions test/project/ProjectPath/Manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This file is machine-generated - editing it directly is not advised

julia_version = "1.12.0-DEV"
manifest_format = "2.0"
project_hash = "51ade905d618e4aa369bc869841376219cc36cb1"

[[deps.ProjectPath]]
deps = ["ProjectPathDep"]
path = "."
entryfile = "CustomPath.jl"
uuid = "32833bde-7fc1-4d28-8365-9d01e1bcbc1b"
version = "0.1.0"

[[deps.ProjectPathDep]]
path = "ProjectPathDep"
entryfile = "CustomPath.jl"
uuid = "f18633fc-8799-43ff-aa06-99ed830dc572"
version = "0.1.0"
7 changes: 7 additions & 0 deletions test/project/ProjectPath/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name = "ProjectPath"
uuid = "32833bde-7fc1-4d28-8365-9d01e1bcbc1b"
entryfile = "CustomPath.jl"
version = "0.1.0"

[deps]
ProjectPathDep = "f18633fc-8799-43ff-aa06-99ed830dc572"
5 changes: 5 additions & 0 deletions test/project/ProjectPath/ProjectPathDep/CustomPath.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module ProjectPathDep

greet() = print("Hello World!")

end # module ProjectPathDep
4 changes: 4 additions & 0 deletions test/project/ProjectPath/ProjectPathDep/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name = "ProjectPathDep"
uuid = "f18633fc-8799-43ff-aa06-99ed830dc572"
version = "0.1.0"
entryfile = "CustomPath.jl"

0 comments on commit 5f4dec1

Please sign in to comment.