Skip to content

Commit aba90d2

Browse files
Merge pull request #4037 from JuliaLang/backports-release-1.11
[release-1.11] 1.11 backports
2 parents 6ceafca + 76eaa4c commit aba90d2

File tree

9 files changed

+107
-43
lines changed

9 files changed

+107
-43
lines changed

docs/src/creating-packages.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ Contour = "d38c429a-6771-53c6-b99e-75d170b6e991"
416416
# name of extension to the left
417417
# extension dependencies required to load the extension to the right
418418
# use a list for multiple extension dependencies
419-
PlottingContourExt = "Contour"
419+
ContourExt = "Contour"
420420

421421
[compat]
422422
Contour = "0.6.2"
@@ -433,9 +433,9 @@ end
433433
end # module
434434
```
435435

436-
`ext/PlottingContourExt.jl` (can also be in `ext/PlottingContourExt/PlottingContourExt.jl`):
436+
`ext/ContourExt.jl` (can also be in `ext/ContourExt/ContourExt.jl`):
437437
```julia
438-
module PlottingContourExt # Should be same name as the file (just like a normal package)
438+
module ContourExt # Should be same name as the file (just like a normal package)
439439

440440
using Plotting, Contour
441441

@@ -446,8 +446,8 @@ end
446446
end # module
447447
```
448448

449-
Extensions can have any arbitrary name (here `PlottingContourExt`), but using something similar to the format of
450-
this example that makes the extended functionality and dependency of the extension clear is likely a good idea.
449+
Extensions can have arbitrary names (here `ContourExt`), following the format of this example is likely a good idea for extensions with a single dependency.
450+
In `Pkg` output, extension names are always shown together with their parent package name.
451451

452452
!!! compat
453453
Often you will put the extension dependencies into the `test` target so they are loaded when running e.g. `Pkg.test()`. On earlier Julia versions
@@ -461,8 +461,8 @@ this example that makes the extended functionality and dependency of the extensi
461461

462462
### Behavior of extensions
463463

464-
A user that depends only on `Plotting` will not pay the cost of the "extension" inside the `PlottingContourExt` module.
465-
It is only when the `Contour` package actually gets loaded that the `PlottingContourExt` extension is loaded too
464+
A user that depends only on `Plotting` will not pay the cost of the "extension" inside the `ContourExt` module.
465+
It is only when the `Contour` package actually gets loaded that the `ContourExt` extension is loaded too
466466
and provides the new functionality.
467467

468468
In our example, the new functionality is an additional _method_, which we add to an existing _function_ from the parent package `Plotting`.
@@ -474,17 +474,17 @@ function plot end
474474
```
475475

476476
!!! note
477-
If one considers `PlottingContourExt` as a completely separate package, it could be argued that defining `Plotting.plot(c::Contour.ContourCollection)` is
478-
[type piracy](https://docs.julialang.org/en/v1/manual/style-guide/#Avoid-type-piracy) since `PlottingContourExt` _owns_ neither the function `Plotting.plot` nor the type `Contour.ContourCollection`.
477+
If one considers `ContourExt` as a completely separate package, it could be argued that defining `Plotting.plot(c::Contour.ContourCollection)` is
478+
[type piracy](https://docs.julialang.org/en/v1/manual/style-guide/#Avoid-type-piracy) since `ContourExt` _owns_ neither the function `Plotting.plot` nor the type `Contour.ContourCollection`.
479479
However, for extensions, it is ok to assume that the extension owns the functions in its parent package.
480480

481481
In other situations, one may need to define new symbols in the extension (types, structs, functions, etc.) instead of reusing those from the parent package.
482-
Such symbols are created in a separate module corresponding to the extension, namely `PlottingContourExt`, and thus not in `Plotting` itself.
482+
Such symbols are created in a separate module corresponding to the extension, namely `ContourExt`, and thus not in `Plotting` itself.
483483
If extension symbols are needed in the parent package, one must call `Base.get_extension` to retrieve them.
484-
Here is an example showing how a custom type defined in `PlottingContourExt` can be accessed in `Plotting`:
484+
Here is an example showing how a custom type defined in `ContourExt` can be accessed in `Plotting`:
485485

486486
```julia
487-
ext = Base.get_extension(@__MODULE__, :PlottingContourExt)
487+
ext = Base.get_extension(@__MODULE__, :ContourExt)
488488
if !isnothing(ext)
489489
ContourPlotType = ext.ContourPlotType
490490
end
@@ -512,7 +512,7 @@ This is done by making the following changes (using the example above):
512512

513513
@static if !isdefined(Base, :get_extension)
514514
function __init__()
515-
@require Contour = "d38c429a-6771-53c6-b99e-75d170b6e991" include("../ext/PlottingContourExt.jl")
515+
@require Contour = "d38c429a-6771-53c6-b99e-75d170b6e991" include("../ext/ContourExt.jl")
516516
end
517517
end
518518
```
@@ -526,11 +526,11 @@ This is done by making the following changes (using the example above):
526526
# Other init functionality here
527527

528528
@static if !isdefined(Base, :get_extension)
529-
@require Contour = "d38c429a-6771-53c6-b99e-75d170b6e991" include("../ext/PlottingContourExt.jl")
529+
@require Contour = "d38c429a-6771-53c6-b99e-75d170b6e991" include("../ext/ContourExt.jl")
530530
end
531531
end
532532
```
533-
- Make the following change in the conditionally-loaded code:
533+
- Make the following change in the conditionally-loaded code in `ContourExt.jl`:
534534
```julia
535535
isdefined(Base, :get_extension) ? (using Contour) : (using ..Contour)
536536
```
@@ -549,7 +549,7 @@ This is done by making the following changes (using the example above):
549549
- Add the following to your main package file (typically at the bottom):
550550
```julia
551551
if !isdefined(Base, :get_extension)
552-
include("../ext/PlottingContourExt.jl")
552+
include("../ext/ContourExt.jl")
553553
end
554554
```
555555

docs/src/getting-started.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ We can see that the `tutorial` environment now contains `Example` and `JSON`.
154154
If you have the same
155155
package (at the same version) installed in multiple environments, the package
156156
will only be downloaded and stored on the hard drive once. This makes environments
157-
very lightweight and effectively free to create. Only using the default
157+
very lightweight and effectively free to create. Using only the default
158158
environment with a huge number of packages in it is a common beginners mistake in
159159
Julia. Learning how to use environments effectively will improve your experience with
160160
Julia packages.

ext/REPLExt/REPLExt.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import .REPL: LineEdit, REPLCompletions, TerminalMenus
77

88
import Pkg
99
import .Pkg: linewrap, pathrepr, compat, can_fancyprint, printpkgstyle, PKGMODE_PROJECT
10-
using .Pkg: Types, Operations, API, Registry, Resolve, REPLMode
10+
using .Pkg: Types, Operations, API, Registry, Resolve, REPLMode, safe_realpath
1111

1212
using .REPLMode: Statement, CommandSpec, Command, prepare_cmd, tokenize, core_parse, SPECS, api_options, parse_option, api_options, is_opt, wrap_option
1313

@@ -47,7 +47,7 @@ function projname(project_file::String)
4747
end
4848
for depot in Base.DEPOT_PATH
4949
envdir = joinpath(depot, "environments")
50-
if startswith(abspath(project_file), abspath(envdir))
50+
if startswith(safe_realpath(project_file), safe_realpath(envdir))
5151
return "@" * name
5252
end
5353
end

src/Operations.jl

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -246,17 +246,14 @@ function reset_all_compat!(proj::Project)
246246
return nothing
247247
end
248248

249-
function collect_project(pkg::PackageSpec, path::String)
249+
function collect_project(pkg::Union{PackageSpec, Nothing}, path::String)
250250
deps = PackageSpec[]
251251
weakdeps = Set{UUID}()
252252
project_file = projectfile_path(path; strict=true)
253-
if project_file === nothing
254-
pkgerror("could not find project file for package $(err_rep(pkg)) at `$path`")
255-
end
256-
project = read_package(project_file)
253+
project = project_file === nothing ? Project() : read_project(project_file)
257254
julia_compat = get_compat(project, "julia")
258255
if !isnothing(julia_compat) && !(VERSION in julia_compat)
259-
pkgerror("julia version requirement from Project.toml's compat section not satisfied for package $(err_rep(pkg)) at `$path`")
256+
pkgerror("julia version requirement from Project.toml's compat section not satisfied for package at `$path`")
260257
end
261258
for (name, uuid) in project.deps
262259
path, repo = get_path_repo(project, name)
@@ -268,11 +265,13 @@ function collect_project(pkg::PackageSpec, path::String)
268265
push!(deps, PackageSpec(name, uuid, vspec))
269266
push!(weakdeps, uuid)
270267
end
271-
if project.version !== nothing
272-
pkg.version = project.version
273-
else
274-
# @warn("project file for $(pkg.name) is missing a `version` entry")
275-
pkg.version = VersionNumber(0)
268+
if pkg !== nothing
269+
if project.version !== nothing
270+
pkg.version = project.version
271+
else
272+
# @warn("project file for $(pkg.name) is missing a `version` entry")
273+
pkg.version = VersionNumber(0)
274+
end
276275
end
277276
return deps, weakdeps
278277
end
@@ -310,13 +309,13 @@ end
310309
function collect_fixed!(env::EnvCache, pkgs::Vector{PackageSpec}, names::Dict{UUID, String})
311310
deps_map = Dict{UUID,Vector{PackageSpec}}()
312311
weak_map = Dict{UUID,Set{UUID}}()
313-
if env.pkg !== nothing
314-
pkg = env.pkg
315-
deps, weakdeps = collect_project(pkg, dirname(env.project_file))
316-
deps_map[pkg.uuid] = deps
317-
weak_map[pkg.uuid] = weakdeps
318-
names[pkg.uuid] = pkg.name
319-
end
312+
313+
uuid = Types.project_uuid(env)
314+
deps, weakdeps = collect_project(env.pkg, dirname(env.project_file))
315+
deps_map[uuid] = deps
316+
weak_map[uuid] = weakdeps
317+
names[uuid] = env.pkg === nothing ? "project" : env.pkg.name
318+
320319
for pkg in pkgs
321320
# add repo package if necessary
322321
if (pkg.repo.rev !== nothing || pkg.repo.source !== nothing) && pkg.tree_hash === nothing
@@ -346,7 +345,8 @@ function collect_fixed!(env::EnvCache, pkgs::Vector{PackageSpec}, names::Dict{UU
346345
idx = findfirst(pkg -> pkg.uuid == uuid, pkgs)
347346
fix_pkg = pkgs[idx]
348347
end
349-
fixed[uuid] = Resolve.Fixed(fix_pkg.version, q, weak_map[uuid])
348+
fixpkgversion = fix_pkg === nothing ? v"0.0.0" : fix_pkg.version
349+
fixed[uuid] = Resolve.Fixed(fixpkgversion, q, weak_map[uuid])
350350
end
351351
return fixed
352352
end
@@ -1403,6 +1403,7 @@ function add(ctx::Context, pkgs::Vector{PackageSpec}, new_git=Set{UUID}();
14031403
assert_can_add(ctx, pkgs)
14041404
# load manifest data
14051405
for (i, pkg) in pairs(pkgs)
1406+
delete!(ctx.env.project.weakdeps, pkg.name)
14061407
entry = manifest_info(ctx.env.manifest, pkg.uuid)
14071408
is_dep = any(uuid -> uuid == pkg.uuid, [uuid for (name, uuid) in ctx.env.project.deps])
14081409
source_path, source_repo = get_path_repo(ctx.env.project, pkg.name)
@@ -1467,6 +1468,7 @@ function develop(ctx::Context, pkgs::Vector{PackageSpec}, new_git::Set{UUID};
14671468
assert_can_add(ctx, pkgs)
14681469
# no need to look at manifest.. dev will just nuke whatever is there before
14691470
for pkg in pkgs
1471+
delete!(ctx.env.project.weakdeps, pkg.name)
14701472
ctx.env.project.deps[pkg.name] = pkg.uuid
14711473
end
14721474
# resolve & apply package versions
@@ -2288,6 +2290,7 @@ function status_ext_info(pkg::PackageSpec, env::EnvCache)
22882290
manifest = env.manifest
22892291
manifest_info = get(manifest, pkg.uuid, nothing)
22902292
manifest_info === nothing && return nothing
2293+
depses = manifest_info.deps
22912294
weakdepses = manifest_info.weakdeps
22922295
exts = manifest_info.exts
22932296
if !isempty(weakdepses) && !isempty(exts)
@@ -2299,10 +2302,14 @@ function status_ext_info(pkg::PackageSpec, env::EnvCache)
22992302
# Check if deps are loaded
23002303
extdeps_info= Tuple{String, Bool}[]
23012304
for extdep in extdeps
2302-
haskey(weakdepses, extdep) ||
2303-
pkgerror(isnothing(pkg.name) ? "M" : "$(pkg.name) has a m",
2304-
"alformed Project.toml, the extension package $extdep is not listed in [weakdeps]")
2305-
uuid = weakdepses[extdep]
2305+
if !(haskey(weakdepses, extdep) || haskey(depses, extdep))
2306+
pkgerror(isnothing(pkg.name) ? "M" : "$(pkg.name) has a malformed Project.toml, ",
2307+
"the extension package $extdep is not listed in [weakdeps] or [deps]")
2308+
end
2309+
uuid = get(weakdepses, extdep, nothing)
2310+
if uuid === nothing
2311+
uuid = depses[extdep]
2312+
end
23062313
loaded = haskey(Base.loaded_modules, Base.PkgId(uuid, extdep))
23072314
push!(extdeps_info, (extdep, loaded))
23082315
end

src/Registry/Registry.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,15 @@ function update(regs::Vector{RegistrySpec}; io::IO=stderr_f(), force::Bool=true,
447447
registry_update_log[string(reg.uuid)] = now()
448448
@label done_tarball_read
449449
else
450+
if reg.name == "General" && Base.get_bool_env("JULIA_PKG_GEN_REG_FMT_CHECK", true)
451+
@info """
452+
The General registry is installed via unpacked tarball.
453+
Consider reinstalling it via the newer faster direct from
454+
tarball format by running:
455+
pkg> registry rm General; registry add General
456+
457+
""" maxlog=1
458+
end
450459
mktempdir() do tmp
451460
try
452461
download_verify_unpack(url, nothing, tmp, ignore_existence = true, io=io)
@@ -465,6 +474,14 @@ function update(regs::Vector{RegistrySpec}; io::IO=stderr_f(), force::Bool=true,
465474
end
466475
elseif isdir(joinpath(reg.path, ".git"))
467476
printpkgstyle(io, :Updating, "registry at " * regpath)
477+
if reg.name == "General" && Base.get_bool_env("JULIA_PKG_GEN_REG_FMT_CHECK", true)
478+
@info """
479+
The General registry is installed via git. Consider reinstalling it via
480+
the newer faster direct from tarball format by running:
481+
pkg> registry rm General; registry add General
482+
483+
""" maxlog=1
484+
end
468485
LibGit2.with(LibGit2.GitRepo(reg.path)) do repo
469486
if LibGit2.isdirty(repo)
470487
push!(errors, (regpath, "registry dirty"))

src/Types.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ Base.@kwdef mutable struct Context
412412
julia_version::Union{VersionNumber,Nothing} = VERSION
413413
end
414414

415-
project_uuid(env::EnvCache) = env.pkg === nothing ? nothing : env.pkg.uuid
415+
project_uuid(env::EnvCache) = env.pkg === nothing ? Base.dummy_uuid(env.project_file) : env.pkg.uuid
416416
collides_with_project(env::EnvCache, pkg::PackageSpec) =
417417
is_project_name(env, pkg.name) || is_project_uuid(env, pkg.uuid)
418418
is_project(env::EnvCache, pkg::PackageSpec) = is_project_uuid(env, pkg.uuid)

test/extensions.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using .Utils
22
using Test
3+
using UUIDs
34

45
@testset "weak deps" begin
56
he_root = joinpath(@__DIR__, "test_packages", "ExtensionExamples", "HasExtensions.jl")
@@ -93,4 +94,24 @@ using Test
9394
@test proj.extras == Dict{String, Base.UUID}("Example" => Base.UUID("7876af07-990d-54b4-ab0e-23690620f79a"))
9495
end
9596
end
97+
98+
isolate(loaded_depot=false) do
99+
mktempdir() do dir
100+
Pkg.Registry.add("General")
101+
path = joinpath(@__DIR__, "test_packages", "TestWeakDepProject")
102+
cp(path, joinpath(dir, "TestWeakDepProject"))
103+
Pkg.activate(joinpath(dir, "TestWeakDepProject"))
104+
Pkg.resolve()
105+
@test Pkg.dependencies()[UUID("2ab3a3ac-af41-5b50-aa03-7779005ae688")].version == v"0.3.26"
106+
107+
# Check that explicitly adding a package that is a weak dep removes it from the set of weak deps
108+
ctx = Pkg.Types.Context()
109+
@test "LogExpFunctions" in keys(ctx.env.project.weakdeps)
110+
@test !("LogExpFunctions" in keys(ctx.env.project.deps))
111+
Pkg.add("LogExpFunctions")
112+
ctx = Pkg.Types.Context()
113+
@test "LogExpFunctions" in keys(ctx.env.project.deps)
114+
@test !("LogExpFunctions" in keys(ctx.env.project.weakdeps))
115+
end
116+
end
96117
end

test/repl.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,4 +734,14 @@ end
734734
end
735735
end
736736

737+
@testset "JuliaLang/julia #55850" begin
738+
tmp_55850 = mktempdir()
739+
tmp_sym_link = joinpath(tmp_55850, "sym")
740+
symlink(tmp_55850, tmp_sym_link; dir_target=true)
741+
withenv("JULIA_DEPOT_PATH" => tmp_sym_link * (Sys.iswindows() ? ";" : ":"), "JULIA_LOAD_PATH" => nothing) do
742+
prompt = readchomp(`$(Base.julia_cmd()[1]) --project=$(dirname(@__DIR__)) --startup-file=no -e "using Pkg, REPL; Pkg.activate(io=devnull); REPLExt = Base.get_extension(Pkg, :REPLExt); print(REPLExt.promptf())"`)
743+
@test prompt == "(@v$(VERSION.major).$(VERSION.minor)) pkg> "
744+
end
745+
end
746+
737747
end # module
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[deps]
2+
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
3+
4+
[weakdeps]
5+
LogExpFunctions = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
6+
7+
[compat]
8+
ForwardDiff = "=0.10.36"
9+
LogExpFunctions = "=0.3.26"

0 commit comments

Comments
 (0)