From da3d38b01e82eaa22fa30c23863e9748fb6989eb Mon Sep 17 00:00:00 2001 From: David Varela <00.varela.david@gmail.com> Date: Tue, 6 Aug 2019 15:14:30 -0700 Subject: [PATCH] Add query interface --- docs/src/api.md | 1 + src/API.jl | 31 +++++++++++++++++++----------- src/Operations.jl | 24 +++++++++++++++++++++++- src/Pkg.jl | 38 ++++++++++++++++++++++++++++++++++--- src/Types.jl | 27 +++++++++++++++++++++++++- test/pkg.jl | 48 +++++++++++++++++++++++++++++------------------ test/repl.jl | 32 ++++++++++++++++--------------- 7 files changed, 152 insertions(+), 49 deletions(-) diff --git a/docs/src/api.md b/docs/src/api.md index e204c1652c..216f13ad23 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -32,6 +32,7 @@ Pkg.gc Pkg.status Pkg.precompile Pkg.setprotocol! +Pkg.dependencies ``` diff --git a/src/API.jl b/src/API.jl index f90fb81538..6c8bd0e092 100644 --- a/src/API.jl +++ b/src/API.jl @@ -20,6 +20,26 @@ preview_info() = printstyled("───── Preview mode ─────\n"; c include("generate.jl") +dependencies() = dependencies(Context()) +function dependencies(ctx::Context)::Dict{UUID, PackageInfo} + pkgs = PackageSpec[] + Operations.load_all_deps!(ctx, pkgs) + find_registered!(ctx.env, UUID[pkg.uuid for pkg in pkgs]) + return Dict(pkg.uuid => Operations.package_info(ctx, pkg) for pkg in pkgs) +end + +project() = project(Context()) +function project(ctx::Context) + ctx.env.pkg !== nothing || pkgerror("Active environment is not a project.") + return ProjectInfo( + name = ctx.env.pkg.name, + uuid = ctx.env.pkg.uuid, + version = ctx.env.pkg.version, + dependencies = ctx.env.project.deps, + path = ctx.env.project_file + ) +end + function check_package_name(x::AbstractString, mode=nothing) if !(occursin(Pkg.REPLMode.name_re, x)) message = "$x is not a valid packagename." @@ -251,17 +271,6 @@ function test(ctx::Context, pkgs::Vector{PackageSpec}; return end -installed() = __installed(PKGMODE_PROJECT) -function __installed(mode::PackageMode=PKGMODE_MANIFEST) - diffs = Display.status(Context(), PackageSpec[], mode=mode, use_as_api=true) - version_status = Dict{String, Union{VersionNumber,Nothing}}() - diffs == nothing && return version_status - for entry in diffs - version_status[entry.name] = entry.new.ver - end - return version_status -end - """ gc(ctx::Context=Context(); collect_delay::Period=Day(30), kwargs...) diff --git a/src/Operations.jl b/src/Operations.jl index d10e03cf77..4951c4a5db 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -49,7 +49,7 @@ is_dep(env::EnvCache, pkg::PackageSpec) = function load_direct_deps!(ctx::Context, pkgs::Vector{PackageSpec}; version::Bool=true) # load rest of deps normally for (name::String, uuid::UUID) in ctx.env.project.deps - pkgs[uuid] === nothing || continue # dont duplicate packages + pkgs[uuid] === nothing || continue # do not duplicate packages entry = manifest_info(ctx.env, uuid) push!(pkgs, entry === nothing ? PackageSpec(;uuid=uuid, name=name) : @@ -66,6 +66,7 @@ end function load_all_deps!(ctx::Context, pkgs::Vector{PackageSpec}; version::Bool=true) for (uuid, entry) in ctx.env.manifest + pkgs[uuid] === nothing || continue # do not duplicate packages push!(pkgs, PackageSpec(name=entry.name, uuid=uuid, path=entry.path, version = version ? something(entry.version, VersionSpec()) : VersionSpec(), repo=entry.repo, tree_hash=entry.tree_hash)) @@ -1323,4 +1324,25 @@ function test(ctx::Context, pkgs::Vector{PackageSpec}; end end +function package_info(ctx::Context, pkg::PackageSpec)::PackageInfo + entry = manifest_info(ctx.env, pkg.uuid) + if entry === nothing + pkgerror("Can not query `$(pkg.name)` because it does not exist in the manifest.", + " Use `Pkg.resolve()` to populate the manifest.") + end + package_info(ctx, pkg, entry) +end + +function package_info(ctx::Context, pkg::PackageSpec, entry::PackageEntry)::PackageInfo + info = PackageInfo( + name = pkg.name, + version = pkg.version != VersionSpec() ? pkg.version : nothing, + ispinned = pkg.pinned, + isdeveloped = pkg.path !== nothing, + source = project_rel_path(ctx, source_path(pkg)), + dependencies = collect(values(entry.deps)), + ) + return info +end + end # module diff --git a/src/Pkg.jl b/src/Pkg.jl index 7fc12d5673..008320b02f 100644 --- a/src/Pkg.jl +++ b/src/Pkg.jl @@ -207,9 +207,6 @@ redirecting to the `build.log` file. """ const build = API.build -# TODO: decide what to do with this -const installed = API.installed - """ Pkg.pin(pkg::Union{String, Vector{String}}) Pkg.pin(pkgs::Union{PackageSpec, Vector{PackageSpec}}) @@ -273,6 +270,41 @@ const develop = API.develop #TODO: Will probably be deprecated for something in PkgDev const generate = API.generate +""" + Pkg.dependencies()::Dict{UUID, PackageInfo} + +Query the dependecy graph. +The result is a `Dict` that maps a package UUID to a `PackageInfo` struct representing the dependency (a package). + +PackageInfo fields: + +| `field` | `Description` | +|:---------------|:-----------------------------------------------------------| +| `name` | The name of the package | +| `version` | The version of the package (this is `Nothing` for stdlibs) | +| `isdeveloped` | Whether a package is directly tracking a directory | +| `ispinned` | Whether a package is pinned | +| `source` | The directory containing the source code for that package | +| `dependencies` | The dependencies of that package as a vector of UUIDs | +""" +const dependencies = API.dependencies + +""" + Pkg.project()::ProjectInfo + +Request a `ProjectInfo` struct which contains information about the active project. + +ProjectInfo fields: +| `field` | `Description` | +|:---------------|:--------------------------------------------------------------------------------------------| +| `name` | The project's name | +| `uuid` | The project's UUID | +| `version` | The project's version | +| `dependencies` | The project's direct dependencies as a `Dict` which maps dependency name to dependency UUID | +| `path` | The location of the project file which defines the active project | +""" +const project = API.project + """ Pkg.instantiate(; verbose = false) diff --git a/src/Types.jl b/src/Types.jl index a19e5f0ffd..4107ae50c5 100644 --- a/src/Types.jl +++ b/src/Types.jl @@ -20,7 +20,7 @@ using SHA export UUID, pkgID, SHA1, VersionRange, VersionSpec, empty_versionspec, Requires, Fixed, merge_requires!, satisfies, ResolverError, - PackageSpec, EnvCache, Context, GitRepo, Context!, get_deps, + PackageSpec, EnvCache, Context, PackageInfo, ProjectInfo, GitRepo, Context!, get_deps, PkgError, pkgerror, has_name, has_uuid, is_stdlib, write_env, write_env_usage, parse_toml, find_registered!, project_resolve!, project_deps_resolve!, manifest_resolve!, registry_resolve!, stdlib_resolve!, handle_repos_develop!, handle_repos_add!, ensure_resolved, instantiate_pkg_repo!, manifest_info, registered_uuids, registered_paths, registered_uuid, registered_name, @@ -1366,4 +1366,29 @@ function write_env(ctx::Context; display_diff=true) write_manifest(env.manifest, env, old_env, ctx; display_diff=display_diff) end +### +### PackageInfo +### + +Base.@kwdef struct PackageInfo + name::String + version::Union{Nothing,VersionNumber} + ispinned::Bool + isdeveloped::Bool + source::String + dependencies::Vector{UUID} +end + +### +### ProjectInfo +### + +Base.@kwdef struct ProjectInfo + name::String + uuid::UUID + version::VersionNumber + dependencies::Dict{String,UUID} + path::String +end + end # module diff --git a/test/pkg.jl b/test/pkg.jl index da6da9b40e..f04f55a735 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -151,21 +151,21 @@ temp_pkg_dir() do project_path @testset "adding and upgrading different versions" begin # VersionNumber Pkg.add(PackageSpec(TEST_PKG.name, v"0.3")) - @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3" + @test Pkg.dependencies()[TEST_PKG.uuid].version == v"0.3" Pkg.add(PackageSpec(TEST_PKG.name, v"0.3.1")) - @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3.1" + @test Pkg.dependencies()[TEST_PKG.uuid].version == v"0.3.1" Pkg.rm(TEST_PKG.name) # VersionRange Pkg.add(PackageSpec(TEST_PKG.name, VersionSpec(VersionRange("0.3.0-0.3.2")))) - @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3.2" + @test Pkg.dependencies()[TEST_PKG.uuid].version == v"0.3.2" # Check that adding another packages doesn't upgrade other packages Pkg.add("Test") - @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3.2" + @test Pkg.dependencies()[TEST_PKG.uuid].version == v"0.3.2" Pkg.update(; level = UPLEVEL_PATCH) - @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3.3" + @test Pkg.dependencies()[TEST_PKG.uuid].version == v"0.3.3" Pkg.update(; level = UPLEVEL_MINOR) - @test Pkg.API.__installed()[TEST_PKG.name].minor != 3 + @test Pkg.dependencies()[TEST_PKG.uuid].version.minor != 3 Pkg.rm(TEST_PKG.name) end @@ -181,27 +181,27 @@ temp_pkg_dir() do project_path @testset "pinning / freeing" begin Pkg.add(TEST_PKG.name) - old_v = Pkg.API.__installed()[TEST_PKG.name] + old_v = Pkg.dependencies()[TEST_PKG.uuid].version Pkg.pin(PackageSpec(TEST_PKG.name, v"0.2")) - @test Pkg.API.__installed()[TEST_PKG.name].minor == 2 + @test Pkg.dependencies()[TEST_PKG.uuid].version.minor == 2 Pkg.update(TEST_PKG.name) - @test Pkg.API.__installed()[TEST_PKG.name].minor == 2 + @test Pkg.dependencies()[TEST_PKG.uuid].version.minor == 2 Pkg.free(TEST_PKG.name) Pkg.update() - @test Pkg.API.__installed()[TEST_PKG.name] == old_v + @test Pkg.dependencies()[TEST_PKG.uuid].version == old_v Pkg.rm(TEST_PKG.name) end @testset "develop / freeing" begin Pkg.add(TEST_PKG.name) - old_v = Pkg.API.__installed()[TEST_PKG.name] + old_v = Pkg.dependencies()[TEST_PKG.uuid].version Pkg.rm(TEST_PKG.name) mktempdir() do devdir withenv("JULIA_PKG_DEVDIR" => devdir) do @test_throws PkgError Pkg.develop(Pkg.PackageSpec(url="bleh", rev="blurg")) Pkg.develop(TEST_PKG.name) @test isinstalled(TEST_PKG) - @test Pkg.API.__installed()[TEST_PKG.name] > old_v + @test Pkg.dependencies()[TEST_PKG.uuid].version > old_v test_pkg_main_file = joinpath(devdir, TEST_PKG.name, "src", TEST_PKG.name * ".jl") @test isfile(test_pkg_main_file) # Pkg #152 @@ -227,7 +227,7 @@ temp_pkg_dir() do project_path @test isfile(joinpath(devdir, TEST_PKG.name, "deps", "deps.jl")) Pkg.test(TEST_PKG.name) Pkg.free(TEST_PKG.name) - @test Pkg.API.__installed()[TEST_PKG.name] == old_v + @test Pkg.dependencies()[TEST_PKG.uuid].version == old_v end end end @@ -239,7 +239,7 @@ temp_pkg_dir() do project_path @testset "stdlibs as direct dependency" begin uuid_pkg = (name = "CRC32c", uuid = UUID("8bf52ea8-c179-5cab-976a-9e18b702a9bc")) Pkg.add("CRC32c") - @test haskey(Pkg.API.__installed(), uuid_pkg.name) + @test haskey(Pkg.dependencies(), TEST_PKG.uuid) Pkg.update() # Disable until fixed in Base # Pkg.test("CRC32c") @@ -336,7 +336,7 @@ end temp_pkg_dir() do project_path @testset "libgit2 downloads" begin Pkg.add(TEST_PKG.name; use_libgit2_for_all_downloads=true) - @test haskey(Pkg.installed(), TEST_PKG.name) + @test haskey(Pkg.dependencies(), TEST_PKG.uuid) @eval import $(Symbol(TEST_PKG.name)) @test_throws SystemError open(pathof(eval(Symbol(TEST_PKG.name))), "w") do io end # check read-only Pkg.rm(TEST_PKG.name) @@ -348,7 +348,7 @@ temp_pkg_dir() do project_path cd(joinpath(dir, "UnregisteredWithProject")) do with_current_env() do Pkg.update() - @test haskey(Pkg.API.__installed(), "Example") + @test haskey(Pkg.dependencies(), TEST_PKG.uuid) end end end @@ -358,12 +358,12 @@ end temp_pkg_dir() do project_path @testset "libgit2 downloads" begin Pkg.add(TEST_PKG.name; use_libgit2_for_all_downloads=true) - @test haskey(Pkg.API.__installed(), TEST_PKG.name) + @test haskey(Pkg.dependencies(), TEST_PKG.uuid) Pkg.rm(TEST_PKG.name) end @testset "tarball downloads" begin Pkg.add("JSON"; use_only_tarballs_for_downloads=true) - @test haskey(Pkg.API.__installed(), "JSON") + @test "JSON" in [pkg.name for (uuid, pkg) in Pkg.dependencies()] Pkg.rm("JSON") end end @@ -790,6 +790,18 @@ end end end end +@testset "query interface basic tests" begin + temp_pkg_dir() do project_path; with_temp_env() do + Pkg.develop("Example") + Pkg.add("Unicode") + Pkg.add("Markdown") + @test length(Pkg.project().dependencies) == 3 + xs = Dict(uuid => pkg for (uuid, pkg) in Pkg.dependencies() if pkg.isdeveloped) + @test length(xs) == 1 + @test xs[TEST_PKG.uuid].ispinned == false + end end +end + include("repl.jl") include("api.jl") include("registry.jl") diff --git a/test/repl.jl b/test/repl.jl index 69553b0e6b..8016a236c9 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -66,7 +66,7 @@ temp_pkg_dir(;rm=false) do project_path; cd(project_path) do; pkg"activate ." pkg"add Example@0.5" @test isinstalled(TEST_PKG) - v = Pkg.API.__installed()[TEST_PKG.name] + v = Pkg.dependencies()[TEST_PKG.uuid].version pkg"rm Example" pkg"add Example, Random" pkg"rm Example Random" @@ -83,15 +83,16 @@ temp_pkg_dir(;rm=false) do project_path; cd(project_path) do; pkg"test Example" @test isinstalled(TEST_PKG) - @test Pkg.API.__installed()[TEST_PKG.name] > v + @test Pkg.dependencies()[TEST_PKG.uuid].version > v pkg = "UnregisteredWithoutProject" p = git_init_package(tmp_pkg_path, joinpath(@__DIR__, "test_packages/$pkg")) Pkg.REPLMode.pkgstr("add $p; precompile") @eval import $(Symbol(pkg)) - @test Pkg.API.__installed()[pkg] == v"0.0" + @test first([info for (uuid, info) in Pkg.dependencies() if info.name == pkg]).version == v"0.0" Pkg.test("UnregisteredWithoutProject") pkg2 = "UnregisteredWithProject" + pkg2_uuid = UUID("58262bb0-2073-11e8-3727-4fe182c12249") p2 = git_init_package(tmp_pkg_path, joinpath(@__DIR__, "test_packages/$pkg2")) Pkg.REPLMode.pkgstr("add $p2") Pkg.REPLMode.pkgstr("pin $pkg2") @@ -99,7 +100,7 @@ temp_pkg_dir(;rm=false) do project_path; cd(project_path) do; # FIXME: why isn't this testing the Pkg after importing, rather than after freeing it #@eval import Example #@eval import $(Symbol(pkg2)) - @test Pkg.API.__installed()[pkg2] == v"0.1.0" + @test Pkg.dependencies()[pkg2_uuid].version == v"0.1.0" Pkg.REPLMode.pkgstr("free $pkg2") @test_throws PkgError Pkg.REPLMode.pkgstr("free $pkg2") Pkg.test("UnregisteredWithProject") @@ -114,7 +115,7 @@ temp_pkg_dir(;rm=false) do project_path; cd(project_path) do; LibGit2.add!(repo, "*") LibGit2.commit(repo, "bump version"; author = TEST_SIG, committer=TEST_SIG) pkg"update" - @test Pkg.API.__installed()[pkg2] == v"0.2.0" + @test Pkg.dependencies()[pkg2_uuid].version == v"0.2.0" Pkg.REPLMode.pkgstr("rm $pkg2") c = LibGit2.commit(repo, "empty commit"; author = TEST_SIG, committer=TEST_SIG) @@ -139,7 +140,7 @@ temp_pkg_dir(;rm=false) do project_path; cd(project_path) do; mktempdir() do depot_dir pushfirst!(DEPOT_PATH, depot_dir) pkg"instantiate" - @test Pkg.API.__installed()[pkg2] == v"0.2.0" + @test Pkg.dependencies()[pkg2_uuid].version == v"0.2.0" end finally empty!(DEPOT_PATH) @@ -180,8 +181,8 @@ temp_pkg_dir() do project_path; cd(project_path) do Pkg.REPLMode.pkgstr("build; precompile") @test Base.find_package("UnregisteredWithProject") == joinpath(p1_new_path, "src", "UnregisteredWithProject.jl") @test Base.find_package("UnregisteredWithoutProject") == joinpath(p2_new_path, "src", "UnregisteredWithoutProject.jl") - @test Pkg.API.__installed()["UnregisteredWithProject"] == v"0.1.0" - @test Pkg.API.__installed()["UnregisteredWithoutProject"] == v"0.0.0" + @test Pkg.dependencies()[UUID("58262bb0-2073-11e8-3727-4fe182c12249")].version == v"0.1.0" + @test Pkg.dependencies()[Pkg.Types.Context().env.project.deps["UnregisteredWithoutProject"]].version == v"0.0.0" Pkg.test("UnregisteredWithoutProject") Pkg.test("UnregisteredWithProject") end @@ -204,8 +205,8 @@ temp_pkg_dir() do project_path; cd(project_path) do mkdir("tests") cd("tests") pkg"develop ../SubModule2" - @test Pkg.API.__installed()["SubModule1"] == v"0.1.0" - @test Pkg.API.__installed()["SubModule2"] == v"0.1.0" + @test Pkg.dependencies()[uuid1].version == v"0.1.0" + @test Pkg.dependencies()[uuid2].version == v"0.1.0" # make sure paths to SubModule1 and SubModule2 are relative manifest = Pkg.Types.Context().env.manifest @test manifest[uuid1].path == "SubModule1" @@ -291,8 +292,8 @@ cd_tempdir() do tmp pkg"activate ." pkg"develop .." # HelloWorld pkg"develop ../SubModule" - @test Pkg.installed()["HelloWorld"] == v"0.1.0" - @test Pkg.installed()["SubModule"] == v"0.1.0" + @test Pkg.dependencies()[uuid1].version == v"0.1.0" + @test Pkg.dependencies()[uuid2].version == v"0.1.0" manifest = Pkg.Types.Context().env.manifest @test manifest[uuid1].path == ".." @test manifest[uuid2].path == joinpath("..", "SubModule") @@ -471,7 +472,8 @@ temp_pkg_dir() do project_path; cd(project_path) do test BigProject test """) - current_json = Pkg.API.installed()["JSON"] + json_uuid = Pkg.project().dependencies["JSON"] + current_json = Pkg.dependencies()[json_uuid].version old_project = read("Project.toml", String) open("Project.toml"; append=true) do io print(io, """ @@ -482,10 +484,10 @@ temp_pkg_dir() do project_path; cd(project_path) do ) end pkg"up" - @test Pkg.API.installed()["JSON"].minor == 18 + @test Pkg.dependencies()[json_uuid].version.minor == 18 write("Project.toml", old_project) pkg"up" - @test Pkg.API.installed()["JSON"] == current_json + @test Pkg.dependencies()[json_uuid].version == current_json end end end; end