Skip to content

Commit a611d57

Browse files
authored
Merge pull request #111 from JuliaTesting/sp/fix-1.12
fix: adjust to Pkg API change in 1.12.4
2 parents 0302400 + 0599742 commit a611d57

6 files changed

Lines changed: 216 additions & 1 deletion

File tree

.github/workflows/CI.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ jobs:
3030
- '1.9'
3131
- '1.10' # current LTS
3232
- '1.11'
33+
- '1.12.3'
3334
- '1.12'
3435
- '1.13-nightly' # TODO: Change this to '1.13' once Julia 1.13.0 has been released
3536
- 'nightly'

src/TestEnv.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ elseif VERSION < v"1.9-"
1616
include("julia-1.8/TestEnv.jl")
1717
elseif VERSION < v"1.11-"
1818
include("julia-1.9/TestEnv.jl")
19-
elseif VERSION < v"1.13-"
19+
elseif VERSION < v"1.12.4-"
2020
include("julia-1.11/TestEnv.jl")
21+
elseif VERSION < v"1.13-"
22+
include("julia-1.12/TestEnv.jl")
2123
else
2224
include("julia-1.13/TestEnv.jl")
2325
end

src/julia-1.12/TestEnv.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Pkg
2+
using Pkg: PackageSpec
3+
using Pkg.Types: Context, ensure_resolved, is_project_uuid, write_env, is_stdlib
4+
using Pkg.Types: Types, projectfile_path, manifestfile_path
5+
using Pkg.Operations: manifest_info, manifest_resolve!, project_deps_resolve!
6+
using Pkg.Operations: manifest_rel_path, project_resolve!
7+
using Pkg.Operations: sandbox, source_path, sandbox_preserve, abspath!
8+
using Pkg.Operations: gen_target_project, isfixed
9+
10+
11+
include("common.jl")
12+
include("activate_do.jl")
13+
include("activate_set.jl")

src/julia-1.12/activate_do.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""
2+
TestEnv.activate(f, [pkg]; allow_reresolve=true)
3+
4+
Activate the test enviroment of `pkg` (defaults to current enviroment), and run `f()`,
5+
then deactivate the enviroment.
6+
This is not useful for many people: Julia is not really designed to have the enviroment
7+
being changed while you are executing code.
8+
However, this *is* useful for anyone doing something like making a alternative to
9+
`Pkg.test()`.
10+
Indeed this is basically extracted from what `Pkg.test()` does.
11+
"""
12+
function activate(f, pkg::AbstractString=current_pkg_name(); allow_reresolve=true)
13+
ctx, pkgspec = ctx_and_pkgspec(pkg)
14+
15+
test_project_override = maybe_gen_project_override!(ctx, pkgspec)
16+
path = pkgspec.path::String
17+
return sandbox(ctx, pkgspec, joinpath(path, "test"), test_project_override; allow_reresolve) do
18+
flush(stdout)
19+
f()
20+
end
21+
end

src/julia-1.12/activate_set.jl

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
2+
# Originally from Pkg.Operations.sandbox
3+
4+
"""
5+
TestEnv.activate([pkg]; allow_reresolve=true)
6+
7+
Activate the test enviroment of `pkg` (defaults to current enviroment).
8+
"""
9+
function activate(pkg::AbstractString=current_pkg_name(); allow_reresolve=true)
10+
ctx, pkgspec = ctx_and_pkgspec(pkg)
11+
# This needs to be first as `gen_target_project` fixes `pkgspec.path` if it is nothing
12+
sandbox_project_override = maybe_gen_project_override!(ctx, pkgspec)
13+
14+
sandbox_path = joinpath(pkgspec.path::String, "test")
15+
sandbox_project = projectfile_path(sandbox_path)
16+
17+
tmp = mktempdir()
18+
tmp_project = projectfile_path(tmp)
19+
tmp_manifest = manifestfile_path(tmp)
20+
21+
# Copy env info over to temp env
22+
if sandbox_project_override !== nothing
23+
Types.write_project(sandbox_project_override, tmp_project)
24+
elseif isfile(sandbox_project)
25+
cp(sandbox_project, tmp_project)
26+
chmod(tmp_project, 0o600)
27+
end
28+
# create merged manifest
29+
# - copy over active subgraph
30+
# - abspath! to maintain location of all deved nodes
31+
working_manifest = abspath!(ctx.env, sandbox_preserve(ctx.env, pkgspec, tmp_project))
32+
33+
# - copy over fixed subgraphs from test subgraph
34+
# really only need to copy over "special" nodes
35+
sandbox_env = Types.EnvCache(projectfile_path(sandbox_path))
36+
sandbox_manifest = abspath!(sandbox_env, sandbox_env.manifest)
37+
for (name, uuid) in sandbox_env.project.deps
38+
entry = get(sandbox_manifest, uuid, nothing)
39+
if entry !== nothing && isfixed(entry)
40+
# Signature changed when workspaces were introduced to Pkg in v1.12 (see Pkg.jl#3841)
41+
subgraph = Pkg.Operations.prune_manifest(sandbox_manifest, VERSION < v"1.12.0-" ? [uuid] : Set([uuid]))
42+
for (uuid, entry) in subgraph
43+
entry_working = get(working_manifest, uuid, nothing)
44+
if entry_working !== nothing && entry_working != entry && (ctx.env.pkg !== nothing && ctx.env.pkg.uuid != uuid)
45+
Pkg.Operations.pkgerror("can not merge projects")
46+
end
47+
working_manifest[uuid] = entry
48+
end
49+
end
50+
end
51+
52+
Types.write_manifest(working_manifest, tmp_manifest)
53+
54+
Base.ACTIVE_PROJECT[] = tmp_project
55+
56+
temp_ctx = Context()
57+
temp_ctx.env.project.deps[pkgspec.name] = pkgspec.uuid
58+
59+
# A hack to get [sources] with relative paths working. We just dive into the project
60+
# context and replace all the relative '{path = ".."}' instances with the corresponding
61+
# absolute paths. `pkgspec.path` is assumed to be relative to the Project.toml file
62+
# that we are activating and has the relative paths.
63+
for source in values(temp_ctx.env.project.sources)
64+
isa(source, Dict) || continue
65+
haskey(source, "path") || continue
66+
base_path = test_dir_has_project_file(temp_ctx, pkgspec) ? joinpath(pkgspec.path, "test") : pkgspec.path
67+
if !isabspath(source["path"])
68+
source["path"] = joinpath(base_path, source["path"])
69+
end
70+
end
71+
72+
try
73+
Pkg.resolve(temp_ctx; io=devnull)
74+
@debug "Using _parent_ dep graph"
75+
catch err# TODO
76+
allow_reresolve || rethrow()
77+
@debug exception=err
78+
@warn "Could not use exact versions of packages in manifest, re-resolving"
79+
temp_ctx.env.manifest.deps = Dict(
80+
uuid => entry for
81+
(uuid, entry) in temp_ctx.env.manifest.deps if isfixed(entry)
82+
)
83+
Pkg.resolve(temp_ctx; io=devnull)
84+
@debug "Using _clean_ dep graph"
85+
end
86+
87+
write_env(temp_ctx.env; update_undo=false)
88+
89+
return Base.active_project()
90+
end

src/julia-1.12/common.jl

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
struct TestEnvError <: Exception
2+
msg::AbstractString
3+
end
4+
5+
function Base.showerror(io::IO, ex::TestEnvError, bt; backtrace=true)
6+
printstyled(io, ex.msg, color=Base.error_color())
7+
end
8+
9+
function current_pkg_name()
10+
ctx = Context()
11+
ctx.env.pkg === nothing && throw(TestEnvError("trying to activate test environment of an unnamed project"))
12+
return ctx.env.pkg.name
13+
end
14+
15+
"""
16+
ctx, pkgspec = ctx_and_pkgspec(pkg::AbstractString)
17+
18+
For a given package name `pkg`, instantiate a `Context` for it, and return that `Context`,
19+
and it's `PackageSpec`.
20+
"""
21+
function ctx_and_pkgspec(pkg::AbstractString)
22+
pkgspec = deepcopy(PackageSpec(pkg))
23+
ctx = Context()
24+
isinstalled!(ctx, pkgspec) || throw(TestEnvError("$pkg not installed 👻"))
25+
Pkg.instantiate(ctx)
26+
return ctx, pkgspec
27+
end
28+
29+
"""
30+
isinstalled!(ctx::Context, pkgspec::Pkg.Types.PackageSpec)
31+
32+
Checks if the package is installed by using `ensure_resolved` from `Pkg/src/Types.jl`.
33+
This function fails if the package is not installed, but here we wrap it in a
34+
try-catch as we may want to test another package after the one that isn't installed.
35+
"""
36+
function isinstalled!(ctx::Context, pkgspec::Pkg.Types.PackageSpec)
37+
project_resolve!(ctx.env, [pkgspec])
38+
project_deps_resolve!(ctx.env, [pkgspec])
39+
manifest_resolve!(ctx.env.manifest, [pkgspec])
40+
41+
try
42+
ensure_resolved(ctx, ctx.env.manifest, [pkgspec])
43+
catch err
44+
err isa MethodError && rethrow()
45+
return false
46+
end
47+
return true
48+
end
49+
50+
51+
function test_dir_has_project_file(ctx, pkgspec)
52+
test_dir = get_test_dir(ctx, pkgspec)
53+
test_dir === nothing && return false
54+
return isfile(joinpath(test_dir, "Project.toml"))
55+
end
56+
57+
"""
58+
get_test_dir(ctx::Context, pkgspec::Pkg.Types.PackageSpec)
59+
60+
Gets the testfile path of the package. Code for each Julia version mirrors that found
61+
in `Pkg/src/Operations.jl`.
62+
"""
63+
function get_test_dir(ctx::Context, pkgspec::Pkg.Types.PackageSpec)
64+
if is_project_uuid(ctx.env, pkgspec.uuid)
65+
pkgspec.path = dirname(ctx.env.project_file)
66+
pkgspec.version = ctx.env.pkg.version
67+
else
68+
is_stdlib(pkgspec.uuid::Base.UUID) && return
69+
entry = manifest_info(ctx.env.manifest, pkgspec.uuid)
70+
pkgspec.version = entry.version
71+
pkgspec.tree_hash = entry.tree_hash
72+
pkgspec.repo = entry.repo
73+
pkgspec.path = entry.path
74+
pkgspec.pinned = entry.pinned
75+
pkgspec.path = Pkg.safe_realpath(manifest_rel_path(ctx.env, source_path(ctx.env.project_file, pkgspec)::String))
76+
end
77+
pkgfilepath = source_path(ctx.env.project_file, pkgspec)::String
78+
return joinpath(pkgfilepath, "test")
79+
end
80+
81+
82+
function maybe_gen_project_override!(ctx, pkgspec)
83+
if !test_dir_has_project_file(ctx, pkgspec)
84+
gen_target_project(ctx, pkgspec, pkgspec.path::String, "test")
85+
else
86+
nothing
87+
end
88+
end

0 commit comments

Comments
 (0)