diff --git a/base/precompilation.jl b/base/precompilation.jl index 98f9b7c4e0419..103a6873e0428 100644 --- a/base/precompilation.jl +++ b/base/precompilation.jl @@ -439,14 +439,48 @@ function precompilepkgs(pkgs::Vector{String}=String[]; append!(project_deps, keys(filter(d->last(d).name in keys(env.project_deps), ext_to_parent))) @debug "precompile: deps collected" + + # An extension effectively depends on another extension if it has all the the + # dependencies of that other extension + function expand_indirect_dependencies(direct_deps) + function visit!(visited, node, all_deps) + if node in visited + return + end + push!(visited, node) + for dep in get(Set{Base.PkgId}, direct_deps, node) + if !(dep in all_deps) + push!(all_deps, dep) + visit!(visited, dep, all_deps) + end + end + end + + indirect_deps = Dict{Base.PkgId, Set{Base.PkgId}}() + for package in keys(direct_deps) + # Initialize a set to keep track of all dependencies for 'package' + all_deps = Set{Base.PkgId}() + visited = Set{Base.PkgId}() + visit!(visited, package, all_deps) + # Update direct_deps with the complete set of dependencies for 'package' + indirect_deps[package] = all_deps + end + return indirect_deps + end + # this loop must be run after the full direct_deps map has been populated - for (pkg, pkg_exts) in parent_to_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 direct_deps # for each manifest dep - if !in(_pkg, keys(ext_to_parent)) && pkg in deps # if not an extension and depends on pkg - append!(deps, pkg_exts) # add the package extensions to deps - filter!(!isequal(pkg), deps) # remove the pkg from deps + indirect_deps = expand_indirect_dependencies(direct_deps) + for (ext, parent) in ext_to_parent + ext_loadable_in_pkg = Dict{Base.PkgId,Bool}() + for pkg in keys(direct_deps) + pkg_is_extension = in(pkg, keys(ext_to_parent)) + ext_loadable_in_pkg[pkg] = !pkg_is_extension && pkg != parent && issubset(direct_deps[ext], indirect_deps[pkg]) + end + for (pkg, ext_loadable) in ext_loadable_in_pkg + if ext_loadable && !any((dep)->ext_loadable_in_pkg[dep], direct_deps[pkg]) + # add an edge if the extension is loadable by pkg, and was not loadable in any + # of the pkg's dependencies + push!(direct_deps[pkg], ext) end end end