Skip to content

Commit

Permalink
feat(dap): support dynamically compiled executable
Browse files Browse the repository at this point in the history
- adds add_dynamic_library_paths
- adds load_rust_types
  • Loading branch information
bas-ie committed Nov 25, 2023
1 parent e0853ea commit 3107caf
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 28 deletions.
4 changes: 3 additions & 1 deletion lua/rustaceanvim/config/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ vim.g.rustaceanvim = vim.g.rustaceanvim

---@class RustaceanDapOpts
---@field adapter? DapExecutableConfig | DapServerConfig | disable | fun():(DapExecutableConfig | DapServerConfig | disable) Defaults to a `DapServerConfig` if `codelldb` is detected, and to a `DapExecutableConfig` if `lldb` is detected. Set to `false` to disable.
---@field auto_generate_source_map fun():boolean | boolean Whether to auto-generate a source map for the standard library.
---@field add_dynamic_library_paths? boolean | fun():boolean Accommodate dynamically-linked targets by passing library paths to lldb. Default: `true`.
---@field auto_generate_source_map? fun():boolean | boolean Whether to auto-generate a source map for the standard library.
---@field load_rust_types? fun():boolean | boolean Whether to get Rust types via initCommands (rustlib/etc/lldb_commands). Default: `true`.

---@alias disable false

Expand Down
53 changes: 35 additions & 18 deletions lua/rustaceanvim/config/internal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ local RustaceanConfig
---@class RustAnalyzerInitializedStatusInternal : RustAnalyzerInitializedStatus
---@field health lsp_server_health_status
---@field quiescent boolean inactive?
---
---@param dap_adapter DapExecutableConfig | DapServerConfig | disable
---@return boolean
local function should_enable_dap_config_value(dap_adapter)
local adapter = types.evaluate(dap_adapter)
if adapter == false then
return false
end
return vim.fn.executable('rustc') == 1
end

---@class RustaceanConfig
local RustaceanDefaultConfig = {
Expand Down Expand Up @@ -194,12 +204,6 @@ local RustaceanDefaultConfig = {
adapter = function()
--- @type DapExecutableConfig | DapServerConfig | disable
local result = false
---@type DapExecutableConfig
local lldb_vscode = {
type = 'executable',
command = 'lldb-vscode',
name = 'lldb',
}
if vim.fn.executable('codelldb') == 1 then
---@cast result DapServerConfig
result = {
Expand All @@ -211,23 +215,36 @@ local RustaceanDefaultConfig = {
args = { '--port', '${port}' },
},
}
elseif vim.fn.executable('lldb-vscode') == 1 then
result = lldb_vscode
elseif vim.fn.executable('lldb-dap') == 1 then
-- On some distributions, it may still have the old name
result = lldb_vscode
result.command = 'lldb-dap'
else
local has_lldb_dap = vim.fn.executable('lldb-dap') == 1
local has_lldb_vscode = vim.fn.executable('lldb-vscode') == 1
if not has_lldb_dap and not has_lldb_vscode then
return result
end
local command = has_lldb_dap and 'lldb-dap' or 'lldb-vscode'
---@cast result DapExecutableConfig
result = {
type = 'executable',
command = command,
name = 'lldb',
}
end
return result
end,
--- Whether to auto-generate a source map for the standard library.
--- Accommodate dynamically-linked targets by passing library paths to lldb.
---@type boolean | fun():boolean
add_dynamic_library_paths = function()
return should_enable_dap_config_value(RustaceanConfig.dap.adapter)
end,
--- Auto-generate a source map for the standard library.
---@type boolean | fun():boolean
auto_generate_source_map = function()
local adapter = types.evaluate(RustaceanConfig.dap.adapter)
if adapter == false then
return false
end
return vim.fn.executable('rustc') == 1
return should_enable_dap_config_value(RustaceanConfig.dap.adapter)
end,
--- Get Rust types via initCommands (rustlib/etc/lldb_commands).
---@type boolean | fun():boolean
load_rust_types = function()
return should_enable_dap_config_value(RustaceanConfig.dap.adapter)
end,
},
}
Expand Down
92 changes: 85 additions & 7 deletions lua/rustaceanvim/dap.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local config = require('rustaceanvim.config.internal')
local compat = require('rustaceanvim.compat')
local shell = require('rustaceanvim.shell')
local types = require('rustaceanvim.types.internal')

local function scheduled_error(err)
Expand Down Expand Up @@ -85,7 +86,7 @@ local function get_rustc_sysroot(callback)
if sc.code ~= 0 or result == nil then
return
end
callback(result)
callback((result:gsub('\n$', '')))
end)
end

Expand Down Expand Up @@ -120,16 +121,85 @@ local function generate_source_map()
end)
end

---@param args RADebuggableArgs
function M.start(args)
vim.notify('Compiling a debug build for debugging. This might take some time...')
---@type {[string]: string}
local init_commands = {}

local function get_lldb_commands(workspace_root)
get_rustc_sysroot(function(rustc_sysroot)
local script_import = 'command script import "'
.. compat.joinpath(rustc_sysroot, 'lib', 'rustlib', 'etc', 'lldb_lookup.py')
.. '"'
local commands_file = compat.joinpath(rustc_sysroot, 'lib', 'rustlib', 'etc', 'lldb_commands')
local file = io.open(commands_file, 'r')
local workspace_root_cmds = {}
if file then
for line in file:lines() do
table.insert(workspace_root_cmds, line)
end
file:close()
end
table.insert(workspace_root_cmds, 1, script_import)
init_commands = vim.tbl_extend('force', init_commands, { [workspace_root] = workspace_root_cmds })
end)
end

---@type string[]
local environment = {}

-- Most succinct description: https://github.com/bevyengine/bevy/issues/2589#issuecomment-1753413600
---@param workspace_root string
local function add_dynamic_library_paths(workspace_root)
compat.system({ 'rustc', '--print', 'target-libdir' }, nil, function(sc)
---@cast sc vim.SystemCompleted
local result = sc.stdout
if sc.code ~= 0 or result == nil then
return
end
local rustc_target_path = (result:gsub('\n$', ''))
local target_path = compat.joinpath(workspace_root, 'target', 'debug', 'deps')
local sep = ':'
local win_sep = ';'
if shell.is_windows() then
table.insert(environment, 'PATH=' .. rustc_target_path .. win_sep .. target_path .. win_sep .. '%PATH%')
elseif shell.is_macos() then
table.insert(
environment,
'DKLD_LIBRARY_PATH=' .. rustc_target_path .. sep .. target_path .. sep .. '$DYLD_LIBRARY_PATH'
)
else
table.insert(
environment,
'LD_LIBRARY_PATH=' .. rustc_target_path .. sep .. target_path .. sep .. '$LD_LIBRARY_PATH'
)
end
end)
end

local function handle_configured_options(args)
local is_generate_source_map_enabled = types.evaluate(config.dap.auto_generate_source_map)
---@cast is_generate_source_map_enabled boolean
if is_generate_source_map_enabled then
generate_source_map()
end

local is_load_rust_types_enabled = types.evaluate(config.dap.load_rust_types)
---@cast is_load_rust_types_enabled boolean
if is_load_rust_types_enabled then
get_lldb_commands(args.workspaceRoot)
end

local is_add_dynamic_library_paths_enabled = types.evaluate(config.dap.add_dynamic_library_paths)
---@cast is_add_dynamic_library_paths_enabled boolean
if is_add_dynamic_library_paths_enabled then
add_dynamic_library_paths(args.workspaceRoot)
end
end

---@param args RADebuggableArgs
function M.start(args)
vim.notify('Compiling a debug build for debugging. This might take some time...')
handle_configured_options(args)

local cargo_args = get_cargo_args_from_runnables_args(args)
local cmd = vim.list_extend({ 'cargo' }, cargo_args)
compat.system(cmd, { cwd = args.workspaceRoot }, function(sc)
Expand Down Expand Up @@ -202,10 +272,18 @@ function M.start(args)
-- https://www.kernel.org/doc/html/latest/admin-guide/LSM/Yama.html
runInTerminal = false,
}
local final_config = is_generate_source_map_enabled
and next(source_map) ~= nil
and vim.tbl_deep_extend('force', dap_config, { sourceMap = tbl_to_tuple_list(source_map) })

local final_config = next(init_commands) ~= nil
and vim.tbl_deep_extend('force', dap_config, { initCommands = init_commands[args.workspaceRoot] })
or dap_config

final_config = next(source_map) ~= nil
and vim.tbl_deep_extend('force', final_config, { sourceMap = tbl_to_tuple_list(source_map) })
or final_config

final_config = next(environment) ~= nil and vim.tbl_deep_extend('force', final_config, { env = environment })
or final_config

-- start debugging
dap.run(final_config)
end)
Expand Down
9 changes: 7 additions & 2 deletions lua/rustaceanvim/shell.lua
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
local M = {}

---@return boolean
local function is_windows()
function M.is_windows()
local sysname = vim.loop.os_uname().sysname
return sysname == 'Windows' or sysname == 'Windows_NT'
end

---@return boolean
function M.is_macos()
return vim.loop.os_uname().sysname == 'Darwin'
end

---@return boolean
local function is_nushell()
---@diagnostic disable-next-line: missing-parameter
Expand All @@ -20,7 +25,7 @@ end
---@param commands string[]
---@return string
function M.chain_commands(commands)
local separator = is_windows() and ' | ' or is_nushell() and ';' or ' && '
local separator = M.is_windows() and ' | ' or is_nushell() and ';' or ' && '
local ret = ''

for i, value in ipairs(commands) do
Expand Down

0 comments on commit 3107caf

Please sign in to comment.