From 4e80a88a1d878789864910b7438ae9b9433f5a1d Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Fri, 26 Jul 2024 15:36:13 +0200 Subject: [PATCH] update --- ext/REPLExt/completions.jl | 2 +- src/Apps/Apps.jl | 165 ++++++++++++++++++++----------------- 2 files changed, 90 insertions(+), 77 deletions(-) diff --git a/ext/REPLExt/completions.jl b/ext/REPLExt/completions.jl index 146367ab09..440192b036 100644 --- a/ext/REPLExt/completions.jl +++ b/ext/REPLExt/completions.jl @@ -168,7 +168,7 @@ end import Pkg: Operations, Types, Apps function complete_installed_apps(options, partial; hint) manifest = try - Types.read_manifest(joinpath(Apps.APP_ENV_FOLDER, "AppManifest.toml")) + Types.read_manifest(joinpath(Apps.app_env_folder(), "AppManifest.toml")) catch err err isa PkgError || rethrow() return String[] diff --git a/src/Apps/Apps.jl b/src/Apps/Apps.jl index 231e66ade0..681c8f5586 100644 --- a/src/Apps/Apps.jl +++ b/src/Apps/Apps.jl @@ -1,32 +1,25 @@ module Apps using Pkg -using Pkg.Types: AppInfo, PackageSpec, Context, EnvCache, PackageEntry, handle_repo_add!, handle_repo_develop!, write_manifest, write_project, +using Pkg.Types: AppInfo, PackageSpec, Context, EnvCache, PackageEntry, Manifest, handle_repo_add!, handle_repo_develop!, write_manifest, write_project, pkgerror using Pkg.Operations: print_single, source_path, update_package_add using Pkg.API: handle_package_input! using TOML, UUIDs import Pkg.Registry -############# -# Constants # -############# +app_env_folder() = joinpath(first(DEPOT_PATH), "environments", "apps") +app_manifest_file() = joinpath(app_env_folder(), "AppManifest.toml") +julia_bin_path() = joinpath(first(DEPOT_PATH), "bin") -# Should use `DEPOT_PATH[1]` instead of `homedir()`? -const APP_ENV_FOLDER = joinpath(homedir(), ".julia", "environments", "apps") -const APP_MANIFEST_FILE = joinpath(APP_ENV_FOLDER, "AppManifest.toml") -const JULIA_BIN_PATH = joinpath(homedir(), ".julia", "bin") +app_context() = Context(env=EnvCache(joinpath(app_env_folder(), "Project.toml"))) -################## -# Helper Methods # -################## - function rm_shim(name; kwargs...) - Base.rm(joinpath(JULIA_BIN_PATH, name); kwargs...) + Base.rm(joinpath(julia_bin_path(), name); kwargs...) end -function handle_project_file(sourcepath) +function get_project(sourcepath) project_file = joinpath(sourcepath, "Project.toml") isfile(project_file) || error("Project file not found: $project_file") @@ -35,13 +28,8 @@ function handle_project_file(sourcepath) return project end -function update_app_manifest(pkg) - manifest = Pkg.Types.read_manifest(APP_MANIFEST_FILE) - manifest.deps[pkg.uuid] = pkg - write_manifest(manifest, APP_MANIFEST_FILE) -end -function overwrite_if_different(file, content) +function overwrite_file_if_different(file, content) if !isfile(file) || read(file, String) != content mkpath(dirname(file)) write(file, content) @@ -76,7 +64,6 @@ function get_max_version_register(pkg::PackageSpec, regs) return (max_v, tree_hash) end -app_context() = Context(env=EnvCache(joinpath(APP_ENV_FOLDER, "Project.toml"))) ################## # Main Functions # @@ -84,6 +71,7 @@ app_context() = Context(env=EnvCache(joinpath(APP_ENV_FOLDER, "Project.toml"))) # TODO: Add functions similar to API that takes name, Vector{String} etc and promotes it to `Vector{PackageSpec}`.. + function add(pkg::String) pkg = PackageSpec(pkg) add(pkg) @@ -95,11 +83,15 @@ function add(pkg::Vector{PackageSpec}) end end + function add(pkg::PackageSpec) handle_package_input!(pkg) ctx = app_context() + manifest = ctx.env.manifest new = false + + # Download package if pkg.repo.source !== nothing || pkg.repo.rev !== nothing entry = Pkg.API.manifest_info(ctx.env.manifest, pkg.uuid) pkg = update_package_add(ctx, pkg, entry, false) @@ -107,42 +99,54 @@ function add(pkg::PackageSpec) else pkgs = [pkg] Pkg.Operations.registry_resolve!(ctx.registries, pkgs) - Pkg.Operations.ensure_resolved(ctx, ctx.env.manifest, pkgs, registry=true) + Pkg.Operations.ensure_resolved(ctx, manifest, pkgs, registry=true) pkg.version, pkg.tree_hash = get_max_version_register(pkg, ctx.registries) new = Pkg.Operations.download_source(ctx, pkgs) end - sourcepath = source_path(ctx.env.manifest_file, pkg) - project = handle_project_file(sourcepath) + sourcepath = source_path(manifest_file, pkg) + project = get_project(sourcepath) # TODO: Wrong if package itself has a sourcepath? - project.entryfile = joinpath(sourcepath, "src", "$(project.name).jl") - # TODO: Type stab - # appdeps = get(project, "appdeps", Dict()) - # merge!(project.deps, appdeps) + entry = PackageEntry(;apps = project.apps, name = pkg.name, version = project.version, tree_hash = pkg.tree_hash, path = pkg.path, repo = pkg.repo, uuid=pkg.uuid) + manifest.deps[pkg.uuid] = entry - projectfile = joinpath(APP_ENV_FOLDER, pkg.name, "Project.toml") - mkpath(dirname(projectfile)) - write_project(project, projectfile) + _resolve(manifest, pkg.name) + @info "For package: $(pkg.name) installed apps $(join(keys(project.apps), ","))" +end - # Move manifest if it exists here. - Pkg.activate(joinpath(APP_ENV_FOLDER, pkg.name)) do - Pkg.instantiate() - end +function _resolve(manifest::Manifest, pkgname=nothing) + for (uuid, pkg) in manifest.deps + if pkgname !== nothing && pkg.name !== pkgname + continue + end + if pkg.path == nothing + projectfile = joinpath(app_env_folder(), pkg.name, "Project.toml") + sourcepath = source_path(app_manifest_file(), pkg) + project = get_project(sourcepath) + project.entryfile = joinpath(sourcepath, "src", "$(project.name).jl") + mkpath(dirname(projectfile)) + write_project(project, projectfile) + # Move manifest if it exists here. + + Pkg.activate(joinpath(app_env_folder(), pkg.name)) do + Pkg.instantiate() + end + else + # TODO: Not hardcode Project.toml + projectfile = joinpath(source_path(app_manifest_file(), pkg), "Project.toml") + @show projectfile + end - if new - # TODO: Call build on the package if it was freshly installed? + # TODO: Julia path + generate_shims_for_apps(pkg.name, pkg.apps, dirname(projectfile), joinpath(Sys.BINDIR, "julia")) end - # Create the new package env. - entry = PackageEntry(;apps = project.apps, name = pkg.name, version = project.version, tree_hash = pkg.tree_hash, path = pkg.path, repo = pkg.repo, uuid=pkg.uuid) - update_app_manifest(entry) - generate_shims_for_apps(entry.name, entry.apps, dirname(projectfile)) + write_manifest(manifest, app_manifest_file()) end - function develop(pkg::String) develop(PackageSpec(pkg)) end @@ -161,7 +165,7 @@ function develop(pkg::PackageSpec) ctx = app_context() handle_repo_develop!(ctx, pkg, #=shared =# true) sourcepath = abspath(source_path(ctx.env.manifest_file, pkg)) - project = handle_project_file(sourcepath) + project = get_project(sourcepath) # Seems like the `.repo.source` field is not cleared. # At least repo-url is still in the manifest after doing a dev with a path @@ -171,9 +175,13 @@ function develop(pkg::PackageSpec) pkg.repo.source = nothing end + entry = PackageEntry(;apps = project.apps, name = pkg.name, version = project.version, tree_hash = pkg.tree_hash, path = sourcepath, repo = pkg.repo, uuid=pkg.uuid) - update_app_manifest(entry) - generate_shims_for_apps(entry.name, entry.apps, sourcepath) + manifest = ctx.env.manifest + manifest.deps[pkg.uuid] = entry + + _resolve(manifest, pkg.name) + @info "For package: $(pkg.name) installed apps: $(join(keys(project.apps), ","))" end function status(pkgs_or_apps::Vector) @@ -193,7 +201,7 @@ function status(pkg_or_app::Union{PackageSpec, Nothing}=nothing) # TODO: Sort. # TODO: Show julia version pkg_or_app = pkg_or_app === nothing ? nothing : pkg_or_app.name - manifest = Pkg.Types.read_manifest(joinpath(APP_ENV_FOLDER, "AppManifest.toml")) + manifest = Pkg.Types.read_manifest(joinpath(app_env_folder(), "AppManifest.toml")) deps = Pkg.Operations.load_manifest_deps(manifest) is_pkg = pkg_or_app !== nothing && any(dep -> dep.name == pkg_or_app, values(manifest.deps)) @@ -223,7 +231,7 @@ end #= function precompile(pkg::Union{Nothing, String}=nothing) - manifest = Pkg.Types.read_manifest(joinpath(APP_ENV_FOLDER, "AppManifest.toml")) + manifest = Pkg.Types.read_manifest(joinpath(app_env_folder(), "AppManifest.toml")) deps = Pkg.Operations.load_manifest_deps(manifest) for dep in deps # TODO: Parallel app compilation..? @@ -231,7 +239,7 @@ function precompile(pkg::Union{Nothing, String}=nothing) if pkg !== nothing && info.name !== pkg continue end - Pkg.activate(joinpath(APP_ENV_FOLDER, info.name)) do + Pkg.activate(joinpath(app_env_folder(), info.name)) do @info "Precompiling $(info.name)..." Pkg.precompile() end @@ -262,18 +270,18 @@ function rm(pkg_or_app::Union{PackageSpec, Nothing}=nothing) require_not_empty(pkg_or_app, :rm) - manifest = Pkg.Types.read_manifest(joinpath(APP_ENV_FOLDER, "AppManifest.toml")) + manifest = Pkg.Types.read_manifest(joinpath(app_env_folder(), "AppManifest.toml")) dep_idx = findfirst(dep -> dep.name == pkg_or_app, manifest.deps) if dep_idx !== nothing dep = manifest.deps[dep_idx] - @info "Deleted all apps for package $(dep.name)" + @info "Deleting all apps for package $(dep.name)" delete!(manifest.deps, dep.uuid) for (appname, appinfo) in dep.apps @info "Deleted $(appname)" rm_shim(appname; force=true) end if dep.path === nothing - Base.rm(joinpath(APP_ENV_FOLDER, dep.name); recursive=true) + Base.rm(joinpath(app_env_folder(), dep.name); recursive=true) end else for (uuid, pkg) in manifest.deps @@ -286,61 +294,68 @@ function rm(pkg_or_app::Union{PackageSpec, Nothing}=nothing) end if isempty(pkg.apps) delete!(manifest.deps, uuid) - Base.rm(joinpath(APP_ENV_FOLDER, pkg.name); recursive=true) + Base.rm(joinpath(app_env_folder(), pkg.name); recursive=true) end end end - Pkg.Types.write_manifest(manifest, APP_MANIFEST_FILE) + Pkg.Types.write_manifest(manifest, app_manifest_file()) return end - ######### # Shims # ######### -function generate_shims_for_apps(pkgname, apps, env) +const SHIM_COMMENT = Sys.iswindows() ? "REM " : "#" +const SHIM_VERSION = 1.0 +const SHIM_HEADER = """$SHIM_COMMENT This file is generated by the Julia package manager. + $SHIM_COMMENT Shim version: $SHIM_VERSION""" + + +function generate_shims_for_apps(pkgname, apps, env, julia) for (_, app) in apps - generate_shim(app, pkgname; env) + generate_shim(pkgname, app, env, julia) end end -function generate_shim(app::AppInfo, pkgname; julia_executable_path::String=joinpath(Sys.BINDIR, "julia"), env=joinpath(homedir(), ".julia", "environments", "apps", pkgname)) +function generate_shim(pkgname, app::AppInfo, env, julia) filename = app.name * (Sys.iswindows() ? ".bat" : "") - julia_bin_filename = joinpath(JULIA_BIN_PATH, filename) + julia_bin_filename = joinpath(julia_bin_path(), filename) mkpath(dirname(filename)) content = if Sys.iswindows() - windows_shim(pkgname, julia_executable_path, env) + windows_shim(pkgname, julia, env) else - bash_shim(pkgname, julia_executable_path, env) + bash_shim(pkgname, julia, env) end - overwrite_if_different(julia_bin_filename, content) + overwrite_file_if_different(julia_bin_filename, content) if Sys.isunix() chmod(julia_bin_filename, 0o755) end end -function bash_shim(pkgname, julia_executable_path::String, env) +function bash_shim(pkgname, julia::String, env) return """ #!/usr/bin/env bash + $SHIM_HEADER + export JULIA_LOAD_PATH=$(repr(env)) - exec $julia_executable_path \\ + exec $julia \\ --startup-file=no \\ -m $(pkgname) \\ "\$@" """ end -function windows_shim(pkgname, julia_executable_path::String, env) +function windows_shim(pkgname, julia::String, env) return """ @echo off set JULIA_LOAD_PATH=$(repr(env)) - $julia_executable_path ^ + $julia ^ --startup-file=no ^ -m $(pkgname) ^ %* @@ -348,8 +363,6 @@ function windows_shim(pkgname, julia_executable_path::String, env) end - - #= ################# # PATH handling # @@ -363,26 +376,26 @@ function add_bindir_to_path() end end -function get_shell_config_file(julia_bin_path) +function get_shell_config_file(julia_bin_path()) home_dir = ENV["HOME"] # Check for various shell configuration files if occursin("/zsh", ENV["SHELL"]) - return (joinpath(home_dir, ".zshrc"), "path=('$julia_bin_path' \$path)\nexport PATH") + return (joinpath(home_dir, ".zshrc"), "path=('$julia_bin_path()' \$path)\nexport PATH") elseif occursin("/bash", ENV["SHELL"]) - return (joinpath(home_dir, ".bashrc"), "export PATH=\"\$PATH:$julia_bin_path\"") + return (joinpath(home_dir, ".bashrc"), "export PATH=\"\$PATH:$julia_bin_path()\"") elseif occursin("/fish", ENV["SHELL"]) - return (joinpath(home_dir, ".config/fish/config.fish"), "set -gx PATH \$PATH $julia_bin_path") + return (joinpath(home_dir, ".config/fish/config.fish"), "set -gx PATH \$PATH $julia_bin_path()") elseif occursin("/ksh", ENV["SHELL"]) - return (joinpath(home_dir, ".kshrc"), "export PATH=\"\$PATH:$julia_bin_path\"") + return (joinpath(home_dir, ".kshrc"), "export PATH=\"\$PATH:$julia_bin_path()\"") elseif occursin("/tcsh", ENV["SHELL"]) || occursin("/csh", ENV["SHELL"]) - return (joinpath(home_dir, ".tcshrc"), "setenv PATH \$PATH:$julia_bin_path") # or .cshrc + return (joinpath(home_dir, ".tcshrc"), "setenv PATH \$PATH:$julia_bin_path()") # or .cshrc else return (nothing, nothing) end end function update_unix_PATH() - shell_config_file, path_command = get_shell_config_file(JULIA_BIN_PATH) + shell_config_file, path_command = get_shell_config_file(julia_bin_path()) if shell_config_file === nothing @warn "Failed to insert `.julia/bin` to PATH: Failed to detect shell" return @@ -411,8 +424,8 @@ end function update_windows_PATH() current_path = ENV["PATH"] - occursin(JULIA_BIN_PATH, current_path) && return - new_path = "$current_path;$JULIA_BIN_PATH" + occursin(julia_bin_path(), current_path) && return + new_path = "$current_path;$julia_bin_path()" run(`setx PATH "$new_path"`) end =#