@@ -471,6 +471,73 @@ function collect_all_deps(direct_deps, dep, alldeps=Set{Base.PkgId}())
471471end
472472
473473
474+ """
475+ precompilepkgs(pkgs; kwargs...)
476+
477+ Precompile packages and their dependencies, with support for parallel compilation,
478+ progress tracking, and various compilation configurations.
479+
480+ `pkgs::Union{Vector{String}, Vector{PkgId}}`: Packages to precompile. When
481+ empty (default), precompiles all project dependencies. When specified,
482+ precompiles only the given packages and their dependencies (unless
483+ `manifest=true`).
484+
485+ # Keyword Arguments
486+ - `internal_call::Bool`: Indicates this is an automatic/internal precompilation call
487+ (e.g., triggered by package loading). When `true`, errors are handled gracefully: in
488+ interactive sessions, errors are stored in `Base.MainInclude.err` instead of throwing;
489+ in non-interactive sessions, errors are printed but not thrown. Default: `false`.
490+
491+ - `strict::Bool`: Controls error reporting scope. When `false` (default), only reports
492+ errors for direct project dependencies.
493+
494+ - `warn_loaded::Bool`: When `true` (default), checks for and warns about packages that are
495+ precompiled but already loaded with a different version. Displays a warning that Julia
496+ needs to be restarted to use the newly precompiled versions.
497+
498+ - `timing::Bool`: When `true` (not default), displays timing information for
499+ each package compilation, but only if compilation might have succeeded.
500+ Disables fancy progress bar output (timing is shown in simple text mode).
501+
502+ - `_from_loading::Bool`: Internal flag indicating the call originated from the
503+ package loading system. When `true` (not default): returns early instead of
504+ throwing when packages are not found; suppresses progress messages when not
505+ in an interactive session; allows packages outside the current environment to
506+ be added as serial precompilation jobs; skips LOADING_CACHE initialization;
507+ and changes cachefile locking behavior.
508+
509+ - `configs::Union{Config,Vector{Config}}`: Compilation configurations to use. Each Config
510+ is a `Pair{Cmd, Base.CacheFlags}` specifying command flags and cache flags. When
511+ multiple configs are provided, each package is precompiled for each configuration.
512+
513+ - `io::IO`: Output stream for progress messages, warnings, and errors. Can be
514+ redirected (e.g., to `devnull` when called from loading in non-interactive mode).
515+
516+ - `fancyprint::Bool`: Controls output format. When `true`, displays an animated progress
517+ bar with spinners. When `false`, instead enables `timing` mode. Automatically
518+ disabled when `timing=true` or when called from loading in non-interactive mode.
519+
520+ - `manifest::Bool`: Controls the scope of packages to precompile. When `false` (default),
521+ precompiles only packages specified in `pkgs` and their dependencies. When `true`,
522+ precompiles all packages in the manifest (workspace mode), typically used by Pkg for
523+ workspace precompile requests.
524+
525+ - `ignore_loaded::Bool`: Controls whether already-loaded packages affect cache
526+ freshness checks. When `false` (not default), loaded package versions are considered when
527+ determining if cache files are fresh.
528+
529+ # Return
530+ - `Vector{String}`: Paths to cache files for the requested packages.
531+ - `Nothing`: precompilation should be skipped
532+
533+ # Notes
534+ - Packages in circular dependency cycles are skipped with a warning.
535+ - Packages with `__precompile__(false)` are skipped if they are from loading to
536+ avoid repeated work on every session.
537+ - Parallel compilation is controlled by `JULIA_NUM_PRECOMPILE_TASKS` environment variable
538+ (defaults to CPU_THREADS + 1, capped at 16, halved on Windows).
539+ - Extensions are precompiled when all their triggers are available in the environment.
540+ """
474541function precompilepkgs (pkgs:: Union{Vector{String}, Vector{PkgId}} = String[];
475542 internal_call:: Bool = false ,
476543 strict:: Bool = false ,
@@ -745,8 +812,7 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
745812 pkg_names = [pkg. name for pkg in project_deps]
746813 end
747814 keep = Set {Base.PkgId} ()
748- for dep in direct_deps
749- dep_pkgid = first (dep)
815+ for dep_pkgid in keys (direct_deps)
750816 if dep_pkgid. name in pkg_names
751817 push! (keep, dep_pkgid)
752818 collect_all_deps (direct_deps, dep_pkgid, keep)
@@ -990,8 +1056,10 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
9901056 notify (was_processed[pkg_config])
9911057 continue
9921058 end
993- # Heuristic for when precompilation is disabled
994- if occursin (r" \b __precompile__\(\s *false\s *\) " , read (sourcepath, String))
1059+ # Heuristic for when precompilation is disabled, which must not over-estimate however for any dependent
1060+ # since it will also block precompilation of all dependents
1061+ if _from_loading && single_requested_pkg && occursin (r" \b __precompile__\(\s *false\s *\) " , read (sourcepath, String))
1062+ Base. @logmsg logcalls " Disabled precompiling $(repr (" text/plain" , pkg)) since the text `__precompile__(false)` was found in file."
9951063 notify (was_processed[pkg_config])
9961064 continue
9971065 end
@@ -1035,7 +1103,13 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
10351103 end
10361104 # for extensions, any extension in our direct dependencies is one we have a right to load
10371105 # for packages, we may load any extension (all possible triggers are accounted for above)
1038- loadable_exts = haskey (ext_to_parent, pkg) ? filter ((dep)-> haskey (ext_to_parent, dep), direct_deps[pkg]) : nothing
1106+ loadable_exts = haskey (ext_to_parent, pkg) ? filter ((dep)-> haskey (ext_to_parent, dep), deps) : nothing
1107+ if ! isempty (deps)
1108+ # if deps is empty, either it doesn't have any (so compiled-modules is
1109+ # irrelevant) or we couldn't compute them (so we actually should attempt
1110+ # serial compile, as the dependencies are not in the parallel list)
1111+ flags = ` $flags --compiled-modules=strict`
1112+ end
10391113 if _from_loading && pkg in requested_pkgids
10401114 # loading already took the cachefile_lock and printed logmsg for its explicit requests
10411115 t = @elapsed ret = begin
@@ -1223,18 +1297,18 @@ function _precompilepkgs(pkgs::Union{Vector{String}, Vector{PkgId}},
12231297 pluralde = n_direct_errs == 1 ? " y" : " ies"
12241298 direct = strict ? " " : " direct "
12251299 err_msg = " The following $n_direct_errs $(direct) dependenc$(pluralde) failed to precompile:\n $(String (take! (err_str))) "
1226- if internal_call # aka. auto-precompilation
1227- if isinteractive ()
1300+ if internal_call # aka. decide which untested code path to run that does some unsafe behavior
1301+ if isinteractive () # XXX : this test is incorrect
12281302 plural1 = length (failed_deps) == 1 ? " y" : " ies"
12291303 println (io, " " , color_string (" $(length (failed_deps)) " , Base. error_color ()), " dependenc$(plural1) errored." )
12301304 println (io, " For a report of the errors see `julia> err`. To retry use `pkg> precompile`" )
1231- setglobal! (Base. MainInclude, :err , PkgPrecompileError (err_msg))
1305+ setglobal! (Base. MainInclude, :err , PkgPrecompileError (err_msg)) # XXX : this call is dangerous
12321306 else
12331307 # auto-precompilation shouldn't throw but if the user can't easily access the
12341308 # error messages, just show them
12351309 print (io, " \n " , err_msg)
12361310 end
1237- else
1311+ else # XXX : crashing is wrong
12381312 println (io)
12391313 throw (PkgPrecompileError (err_msg))
12401314 end
0 commit comments