aboutsummaryrefslogtreecommitdiffstats
path: root/lua/mason-core/installer/linker.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/mason-core/installer/linker.lua')
-rw-r--r--lua/mason-core/installer/linker.lua152
1 files changed, 108 insertions, 44 deletions
diff --git a/lua/mason-core/installer/linker.lua b/lua/mason-core/installer/linker.lua
index a1cc53d0..6de95160 100644
--- a/lua/mason-core/installer/linker.lua
+++ b/lua/mason-core/installer/linker.lua
@@ -1,62 +1,85 @@
local path = require "mason-core.path"
+local Result = require "mason-core.result"
local platform = require "mason-core.platform"
local _ = require "mason-core.functional"
local log = require "mason-core.log"
local fs = require "mason-core.fs"
+local a = require "mason-core.async"
local M = {}
----@param pkg Package
----@param links InstallReceiptLinks
-local function unlink_bin(pkg, links)
- for executable in pairs(links.bin) do
+---@param receipt InstallReceipt
+local function unlink_bin(receipt)
+ local bin = receipt.links.bin
+ if not bin then
+ return
+ end
+ -- Windows executables did not include file extension in bin receipts on 1.0.
+ local should_append_cmd = platform.is.win and receipt.schema_version == "1.0"
+ for executable in pairs(bin) do
+ if should_append_cmd then
+ executable = executable .. ".cmd"
+ end
local bin_path = path.bin_prefix(executable)
fs.sync.unlink(bin_path)
end
end
----@param pkg Package
----@param links InstallReceiptLinks
-function M.unlink(pkg, links)
- log.fmt_debug("Unlinking %s", pkg)
- unlink_bin(pkg, links)
+---@param receipt InstallReceipt
+local function unlink_share(receipt)
+ local share = receipt.links.share
+ if not share then
+ return
+ end
+ for share_file in pairs(share) do
+ local bin_path = path.share_prefix(share_file)
+ fs.sync.unlink(bin_path)
+ end
end
----@param to string
-local function relative_path_from_bin(to)
- local _, match_end = to:find(path.install_prefix(), 1, true)
- assert(match_end, "Failed to produce relative path.")
- local relative_path = to:sub(match_end + 1)
- return ".." .. relative_path
+---@param pkg Package
+---@param receipt InstallReceipt
+function M.unlink(pkg, receipt)
+ log.fmt_debug("Unlinking %s", pkg, receipt.links)
+ unlink_bin(receipt)
+ unlink_share(receipt)
end
---@async
---@param context InstallContext
local function link_bin(context)
- local links = context.bin_links
- local pkg = context.package
- for name, rel_path in pairs(links) do
- local target_abs_path = path.concat { pkg:get_install_path(), rel_path }
- local target_rel_path = relative_path_from_bin(target_abs_path)
- local bin_path = path.bin_prefix(name)
+ return Result.try(function(try)
+ local links = context.links.bin
+ local pkg = context.package
+ for name, rel_path in pairs(links) do
+ if platform.is.win then
+ name = ("%s.cmd"):format(name)
+ end
+ local target_abs_path = path.concat { pkg:get_install_path(), rel_path }
+ local bin_path = path.bin_prefix(name)
- assert(not fs.async.file_exists(bin_path), ("bin/%s is already linked."):format(name))
- assert(fs.async.file_exists(target_abs_path), ("Link target %q does not exist."):format(target_abs_path))
+ if not context.opts.force and fs.async.file_exists(bin_path) then
+ return Result.failure(("bin/%s is already linked."):format(name))
+ end
+ if not fs.async.file_exists(target_abs_path) then
+ return Result.failure(("Link target %q does not exist."):format(target_abs_path))
+ end
- log.fmt_debug("Linking bin %s to %s", name, target_rel_path)
+ log.fmt_debug("Linking bin %s to %s", name, target_abs_path)
- platform.when {
- unix = function()
- fs.async.symlink(target_rel_path, bin_path)
- end,
- win = function()
- -- We don't "symlink" on Windows because:
- -- 1) .LNK is not commonly found in PATHEXT
- -- 2) some executables can only run from their true installation location
- -- 3) many utilities only consider .COM, .EXE, .CMD, .BAT files as candidates by default when resolving executables (e.g. neovim's |exepath()| and |executable()|)
- fs.async.write_file(
- ("%s.cmd"):format(bin_path),
- _.dedent(([[
+ platform.when {
+ unix = function()
+ try(Result.pcall(fs.async.symlink, target_abs_path, bin_path))
+ end,
+ win = function()
+ -- We don't "symlink" on Windows because:
+ -- 1) .LNK is not commonly found in PATHEXT
+ -- 2) some executables can only run from their true installation location
+ -- 3) many utilities only consider .COM, .EXE, .CMD, .BAT files as candidates by default when resolving executables (e.g. neovim's |exepath()| and |executable()|)
+ try(Result.pcall(
+ fs.async.write_file,
+ bin_path,
+ _.dedent(([[
@ECHO off
GOTO start
:find_dp0
@@ -66,20 +89,61 @@ local function link_bin(context)
SETLOCAL
CALL :find_dp0
- endLocal & goto #_undefined_# 2>NUL || title %%COMSPEC%% & "%%dp0%%\%s" %%*
- ]]):format(target_rel_path))
- )
- end,
- }
- context.receipt:with_link("bin", name, rel_path)
- end
+ endLocal & goto #_undefined_# 2>NUL || title %%COMSPEC%% & "%s" %%*
+ ]]):format(target_abs_path))
+ ))
+ end,
+ }
+ context.receipt:with_link("bin", name, rel_path)
+ end
+ end)
+end
+
+---@async
+---@param context InstallContext
+local function link_share(context)
+ return Result.try(function(try)
+ for name, rel_path in pairs(context.links.share) do
+ local dest = path.share_prefix(name)
+
+ do
+ if vim.in_fast_event() then
+ a.scheduler()
+ end
+
+ local dir = vim.fn.fnamemodify(dest, ":h")
+ if not fs.async.dir_exists(dir) then
+ try(Result.pcall(fs.async.mkdirp, dir))
+ end
+ end
+
+ local target_abs_path = path.concat { context.package:get_install_path(), rel_path }
+
+ if context.opts.force then
+ if fs.async.file_exists(dest) then
+ try(Result.pcall(fs.async.unlink, dest))
+ end
+ elseif fs.async.file_exists(dest) then
+ return Result.failure(("share/%s is already linked."):format(name))
+ end
+ if not fs.async.file_exists(target_abs_path) then
+ return Result.failure(("Link target %q does not exist."):format(target_abs_path))
+ end
+
+ try(Result.pcall(fs.async.symlink, target_abs_path, dest))
+ context.receipt:with_link("share", name, rel_path)
+ end
+ end)
end
---@async
---@param context InstallContext
function M.link(context)
log.fmt_debug("Linking %s", context.package)
- link_bin(context)
+ return Result.try(function(try)
+ try(link_bin(context))
+ try(link_share(context))
+ end)
end
return M