1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
local path = require "mason-core.path"
local platform = require "mason-core.platform"
local _ = require "mason-core.functional"
local log = require "mason-core.log"
local fs = require "mason-core.fs"
local M = {}
---@param pkg Package
---@param links InstallReceiptLinks
local function unlink_bin(pkg, links)
for executable in pairs(links.bin) do
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)
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
end
---@async
---@param context InstallContext
local function link_bin(context)
local links = context.receipt.links.bin
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)
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))
log.fmt_debug("Linking bin %s to %s", name, target_rel_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(([[
@ECHO off
GOTO start
:find_dp0
SET dp0=%%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
endLocal & goto #_undefined_# 2>NUL || title %%COMSPEC%% & "%%dp0%%\%s" %%*
]]):format(target_rel_path))
)
end,
}
end
end
---@async
---@param context InstallContext
function M.link(context)
log.fmt_debug("Linking %s", context.package)
link_bin(context)
end
return M
|