diff --git a/docs/src/creating-packages.md b/docs/src/creating-packages.md index 3f64815dd1..0b4f858420 100644 --- a/docs/src/creating-packages.md +++ b/docs/src/creating-packages.md @@ -354,8 +354,8 @@ end end # module ``` -The name of the extension (here `PlottingContourExt`) is not very important but using something similar to the suggest here is likely a good -idea. +Extensions can have any arbitrary name (here `PlottingContourExt`), but using something similar to the format of +this example that makes the extended functionality and dependency of the extension clear is likely a good idea. A user that depends only on `Plotting` will not pay the cost of the "extension" inside the `PlottingContourExt` module. It is only when the `Contour` package actually gets loaded that the `PlottingContourExt` extension is loaded @@ -366,8 +366,6 @@ If one considers `PlottingContourExt` as a completely separate package, it could However, for extensions, it is ok to assume that the extension owns the methods in its parent package. In fact, this form of type piracy is one of the most standard use cases for extensions. -An extension will only be loaded if the extension dependencies are loaded from the same environment or environments higher in the environment stack than the package itself. - !!! compat 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 this requires you to also put the package in the `[extras]` section. This is unfortunate but the project verifier on older Julia versions will diff --git a/src/API.jl b/src/API.jl index 14536c8a59..d00ab063ec 100644 --- a/src/API.jl +++ b/src/API.jl @@ -1137,7 +1137,7 @@ function precompile(ctx::Context, pkgs::Vector{PackageSpec}; internal_call::Bool ] stale_cache = Dict{StaleCacheKey, Bool}() exts = Dict{Base.PkgId, String}() # ext -> parent - # make a flat map of each dep and its deps + # make a flat map of each dep and its direct deps depsmap = Dict{Base.PkgId, Vector{Base.PkgId}}() pkg_specs = PackageSpec[] for dep in ctx.env.manifest @@ -1147,6 +1147,7 @@ function precompile(ctx::Context, pkgs::Vector{PackageSpec}; internal_call::Bool depsmap[pkg] = filter!(!Base.in_sysimage, deps) # add any extensions weakdeps = last(dep).weakdeps + pkg_exts = Dict{Base.PkgId, Vector{Base.PkgId}}() for (ext_name, extdep_names) in last(dep).exts ext_deps = Base.PkgId[] push!(ext_deps, pkg) # depends on parent package @@ -1165,8 +1166,26 @@ function precompile(ctx::Context, pkgs::Vector{PackageSpec}; internal_call::Bool ext_uuid = Base.uuid5(pkg.uuid, ext_name) ext = Base.PkgId(ext_uuid, ext_name) push!(pkg_specs, PackageSpec(uuid = ext_uuid, name = ext_name)) # create this here as the name cannot be looked up easily later via the uuid - depsmap[ext] = filter!(!Base.in_sysimage, ext_deps) + filter!(!Base.in_sysimage, ext_deps) + depsmap[ext] = ext_deps exts[ext] = pkg.name + pkg_exts[ext] = ext_deps + end + if !isempty(pkg_exts) + # find any packages that depend on the extension(s)'s deps and replace those deps in their deps list with the extension(s), + # basically injecting the extension into the precompile order in the graph, to avoid race to precompile extensions + for (_pkg, deps) in depsmap # for each manifest dep + if !in(_pkg, keys(exts)) # if not an extension + all_ext_deps = Base.PkgId[] + for (ext, ext_deps) in pkg_exts # for each extension of this package + if any(in(ext_deps), deps) # if any of the outer package deps are extension deps + push!(deps, ext) # add the extension to deps + append!(all_ext_deps, ext_deps) # collect all extension deps for later filtering + end + end + filter!(!in(all_ext_deps), deps) # remove the extension deps once all extensions have been added because they could share deps + end + end end end @@ -1261,7 +1280,7 @@ function precompile(ctx::Context, pkgs::Vector{PackageSpec}; internal_call::Bool printloop_should_exit::Bool = !fancyprint # exit print loop immediately if not fancy printing interrupted_or_done = Base.Event() - function color_string(cstr::String, col::Symbol) + function color_string(cstr::String, col::Union{Int64, Symbol}) enable_ansi = get(Base.text_colors, col, Base.text_colors[:default]) disable_ansi = get(Base.disable_text_style, col, Base.text_colors[:default]) return string(enable_ansi, cstr, disable_ansi) diff --git a/src/Operations.jl b/src/Operations.jl index 14c09df9c9..e9b89d5a9e 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -1659,6 +1659,20 @@ function gen_test_precompile_code(source_path::String; coverage, julia_args::Cmd return gen_subprocess_cmd(code, source_path; coverage, julia_args) end +@static if VERSION >= v"1.9.0" # has threadpools +function get_threads_spec() + if Threads.nthreads(:interactive) > 0 + "$(Threads.nthreads(:default)),$(Threads.nthreads(:interactive))" + else + "$(Threads.nthreads(:default))" + end +end +else # no threadpools +function get_threads_spec() + "$(Threads.nthreads())" +end +end + function gen_subprocess_cmd(code::String, source_path::String; coverage, julia_args) coverage_arg = if coverage isa Bool coverage ? string("@", source_path) : "none" @@ -1677,7 +1691,7 @@ function gen_subprocess_cmd(code::String, source_path::String; coverage, julia_a --inline=$(Bool(Base.JLOptions().can_inline) ? "yes" : "no") --startup-file=$(Base.JLOptions().startupfile == 1 ? "yes" : "no") --track-allocation=$(("none", "user", "all")[Base.JLOptions().malloc_log + 1]) - --threads=$(Threads.nthreads()) + --threads=$(get_threads_spec()) $(julia_args) --eval $(code) ``` diff --git a/src/Pkg.jl b/src/Pkg.jl index eea7562870..5c06fd1aef 100644 --- a/src/Pkg.jl +++ b/src/Pkg.jl @@ -589,7 +589,7 @@ This includes: * The `name` of the package. * The package's unique `uuid`. * A `version` (for example when adding a package). When upgrading, can also be an instance of - the enum [`UpgradeLevel`](@ref). If the version is given as a `String`` this means that unspecified versions + the enum [`UpgradeLevel`](@ref). If the version is given as a `String` this means that unspecified versions are "free", for example `version="0.5"` allows any version `0.5.x` to be installed. If given as a `VersionNumber`, the exact version is used, for example `version=v"0.5.3"`. * A `url` and an optional git `rev`ision. `rev` can be a branch name or a git commit SHA1. diff --git a/src/REPLMode/REPLMode.jl b/src/REPLMode/REPLMode.jl index 99bfa55d3e..032a1b90be 100644 --- a/src/REPLMode/REPLMode.jl +++ b/src/REPLMode/REPLMode.jl @@ -746,7 +746,11 @@ function try_prompt_pkg_add(pkgs::Vector{Symbol}) push!(shown_envs, expanded_env) end menu = TerminalMenus.RadioMenu(option_list, keybindings=keybindings, pagesize=length(option_list)) - default = min(2, length(option_list)) + default = something( + # select the first non-default env by default, if possible + findfirst(!=(Base.active_project()), shown_envs), + 1 + ) print(ctx.io, "\e[1A\e[1G\e[0J") # go up one line, to the start, and clear it printstyled(ctx.io, " └ "; color=:green) choice = try