diff --git a/docs/src/api.md b/docs/src/api.md index 0132085779..ffbfd34143 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -32,6 +32,7 @@ Pkg.gc Pkg.status Pkg.precompile Pkg.setprotocol! +Pkg.info ``` diff --git a/src/API.jl b/src/API.jl index 70a9dcacdd..9856ca7c1f 100644 --- a/src/API.jl +++ b/src/API.jl @@ -18,6 +18,28 @@ preview_info() = printstyled("───── Preview mode ─────\n"; c include("generate.jl") +info(;kwargs...) = info(Context(), PackageSpec[]; kwargs...) +info(uuid::UUID; kwargs...) = info([uuid]; kwargs...) +info(uuids::Vector{UUID}; kwargs...) = info(Context(), [PackageSpec(;uuid=uuid) for uuid in uuids]; kwargs...) +function info(ctx::Context, pkgs::Vector{PackageSpec}; + active=false, direct=false, all=false, kwargs... + )::Dict{UUID, PackageInfo} + if active + if ctx.env.pkg === nothing + @warn "Active environment is not a project" + else + pkg = deepcopy(ctx.env.pkg) + pkg.path = dirname(ctx.env.project_file) + push!(pkgs, pkg) + end + end + direct && Operations.load_direct_deps!(ctx, pkgs) + all && Opeartions.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; kwargs...) for pkg in pkgs) +end + function check_package_name(x::AbstractString, mode=nothing) if !(occursin(Pkg.REPLMode.name_re, x)) message = "$x is not a valid packagename." @@ -246,17 +268,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 - function gc(ctx::Context=Context(); kwargs...) Context!(ctx; kwargs...) ctx.preview && preview_info() diff --git a/src/Operations.jl b/src/Operations.jl index b78fb4e3eb..6a610359d0 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -64,6 +64,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 # dont 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)) @@ -1291,4 +1292,16 @@ function test(ctx::Context, pkgs::Vector{PackageSpec}; coverage=false, test_fn=n end end +function package_info(ctx::Context, pkg::PackageSpec; + name=false, version=false, pinned=false, developed=false, source=false + )::PackageInfo + info = PackageInfo() + name && (info.name = pkg.name) + version && (info.version = (pkg.version == VersionSpec() ? nothing : pkg.version)) + pinned && (info.pinned = pkg.pinned) + source && (info.source = project_rel_path(ctx, source_path(pkg))) + developed && (info.developed = pkg.path !== nothing) + return info +end + end # module diff --git a/src/Pkg.jl b/src/Pkg.jl index c3ae7db859..3ae79ed88a 100644 --- a/src/Pkg.jl +++ b/src/Pkg.jl @@ -198,9 +198,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}}) @@ -264,6 +261,47 @@ const develop = API.develop #TODO: Will probably be deprecated for something in PkgDev const generate = API.generate +""" + Pkg.info(; keywords...)::Dict{UUID, PackageInfo} + Pkg.info(uuid::UUID; keywords...)::Dict{UUID, PackageInfo} + Pkg.info(uuids::Vector{UUID}; keywords...)::Dict{UUID, PackageInfo} + +Query the dependecy graph. +You can choose which packages to query by specyfing a list of uuids or by using *selector* keywords. + +`Pkg.info` accepts two classes of keywords **selectors** and **properties**. +**Selectors** will add certain packages to the set of packages to be queried. +**Properties** determine which package properties will be queried and written to the `PackageInfo` structs. +`nothing` is used as the default value of `PackageInfo` fields when no query has been executed. + +Selectors: + +| `keyword` | `API` | +|:----------|:------------------------------------------------------------------------------------| +| `active` | If the active environment is a project, add it to the set of packages to be queried | +| `direct` | Add all direct dependecies to the set of packages to be queried | +| `all` | Add all dependencies to the set of packages to be queried | + +Properites: + +| `keyword` | `Type` | `Description` | +|:------------|:----------------|:---------------------------------------------------| +| `name` | `String` | The name of the package | +| `version` | `VersionNumber` | The version of the package | +| `developed` | `Bool` | Whether a package is directly tracking a directory | +| `pinned` | `Bool` | Whether a package is pinned | +| `source` | `String` | The directory containing the package's source code | + +# Examples +``` +Pkg.info(;direct=true, name=true, version=true) +Pkg.info(UUID("7876af07-990d-54b4-ab0e-23690620f79a"); developed=true) +Pkg.info(UUID("7876af07-990d-54b4-ab0e-23690620f79a"); direct=true, developed=true) +``` + +""" +const info = API.info + """ Pkg.instantiate() diff --git a/src/Types.jl b/src/Types.jl index 7588a3e02f..6ecac82333 100644 --- a/src/Types.jl +++ b/src/Types.jl @@ -19,7 +19,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, GitRepo, Context!, get_deps, PkgError, pkgerror, has_name, has_uuid, is_stdlib, write_env, 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, @@ -1348,4 +1348,16 @@ function write_env(ctx::Context; display_diff=true) write_manifest(env.manifest, env, old_env, ctx; display_diff=display_diff) end +### +### PackageInfo +### + +Base.@kwdef mutable struct PackageInfo + name::Union{Nothing,String} = nothing + version::Union{Nothing,VersionNumber} = nothing + pinned::Union{Nothing,Bool} = nothing + developed::Union{Nothing,Bool} = nothing + source::Union{Nothing,String} = nothing +end + end # module diff --git a/test/pkg.jl b/test/pkg.jl index d9ca5e83b2..b7fd893030 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -147,21 +147,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.info(;direct=true,version=true)[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.info(;direct=true,version=true)[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.info(;direct=true,version=true)[TEST_PKG.name].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.info(;direct=true,version=true)[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.info(;direct=true,version=true)[TEST_PKG.uuid].version == v"0.3.3" Pkg.update(; level = UPLEVEL_MINOR) - @test Pkg.API.__installed()[TEST_PKG.name].minor != 3 + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version.minor != 3 Pkg.rm(TEST_PKG.name) end @@ -177,27 +177,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.info(;direct=true,version=true)[TEST_PKG.uuid].version Pkg.pin(PackageSpec(TEST_PKG.name, v"0.2")) - @test Pkg.API.__installed()[TEST_PKG.name].minor == 2 + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version.minor == 2 Pkg.update(TEST_PKG.name) - @test Pkg.API.__installed()[TEST_PKG.name].minor == 2 + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version.minor == 2 Pkg.free(TEST_PKG.name) Pkg.update() - @test Pkg.API.__installed()[TEST_PKG.name] == old_v + @test Pkg.info(;direct=true,version=true)[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.info(;direct=true,version=true)[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.info(;direct=true,version=true)[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 @@ -223,7 +223,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.info(;direct=true,version=true)[TEST_PKG.uuid].version == old_v end end end @@ -235,7 +235,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.info(;direct=true,version=true), TEST_PKG.uuid) Pkg.update() # Disable until fixed in Base # Pkg.test("CRC32c") @@ -332,7 +332,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.info(;direct=true,version=true), 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) @@ -344,7 +344,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.info(;direct=true), TEST_PKG.uuid) end end end @@ -354,12 +354,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.info(;direct=true), 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.info(;direct=true,name=true)] Pkg.rm("JSON") end end @@ -783,6 +783,23 @@ 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") + info = Pkg.info(;direct=true, name=true, developed=true) + @test length(info) == 3 + xs = Dict(uuid => pkg for (uuid, pkg) in info if pkg.developed) + @test length(xs) == 1 + @test xs[TEST_PKG.uuid].name == "Example" + @test xs[TEST_PKG.uuid].pinned == nothing + @test xs[TEST_PKG.uuid].version == nothing + @test xs[TEST_PKG.uuid].developed == true + @test xs[TEST_PKG.uuid].source == nothing + end end +end + include("repl.jl") include("api.jl") include("registry.jl")