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 18, 2023
1 parent 7f8feb0 commit a58b7c7
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 29 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 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
51 changes: 33 additions & 18 deletions lua/rustaceanvim/config/internal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ local RustaceanConfig
---@field health lsp_server_health_status
---@field quiescent boolean inactive?

local function should_set_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 = {
---@class RustaceanToolsConfig
Expand Down Expand Up @@ -194,12 +202,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 +213,36 @@ local RustaceanDefaultConfig = {
args = { '--port', '${port}' },
},
}
elseif vim.fn.executable('lldb-vscode') then
result = lldb_vscode
elseif vim.fn.executable('lldb-dap') 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') ~= 0
local has_lldb_vscode = vim.fn.executable('lldb-vscode') ~= 0
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
add_dynamic_library_paths = function()
return should_set_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_set_dap_config_value(RustaceanConfig.dap.adapter)
end,
--- Get Rust types via initCommands (rustlib/etc/lldb_commands).
---@type boolean
load_rust_types = function()
return should_set_dap_config_value(RustaceanConfig.dap.adapter)
end,
},
}
Expand Down
88 changes: 79 additions & 9 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 @@ -70,7 +71,7 @@ local function get_rustc_commit_hash(callback)
if sc.code ~= 0 or result == nil then
return
end
local commit_hash = result:match('commit%-hash:%s+([^\n]+)$')
local commit_hash = result:match('commit%-hash:%s+([^\n]+)')
if not commit_hash then
return
end
Expand All @@ -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 All @@ -102,21 +103,80 @@ local function generate_source_map()
local new_map = {
[compat.joinpath('/rustc/', commit_hash)] = compat.joinpath(rustc_sysroot, '/lib/rustlib/src/rust'),
}
vim.tbl_extend('force', source_map, { new_map })
source_map = vim.tbl_extend('force', source_map, { new_map })
end)
end)
end

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

local function get_lldb_commands()
get_rustc_sysroot(function(rustc_sysroot)
local script_import = 'command script import "' .. rustc_sysroot .. '/lib/rustlib/etc/lldb_lookup.py"'
local commands_file = rustc_sysroot .. '/lib/rustlib/etc/lldb_commands'
local file = io.open(commands_file, 'r')
if file then
for line in file:lines() do
table.insert(init_commands, line)
end
file:close()
end
table.insert(init_commands, 1, script_import)
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 = 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)
else
table.insert(environment, 'LD_LIBRARY_PATH=' .. rustc_target_path .. sep .. target_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()
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 @@ -170,6 +230,9 @@ function M.start(args)
-- create debug configuration
local dap_config = {
name = 'Rust tools debug',

-- TODO: arguably, this type should just be 'lldb`. Is there an important reason to
-- use a custom type for the plugin?
type = 'rt_lldb',
request = 'launch',
program = executables[1],
Expand All @@ -189,10 +252,17 @@ 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 = source_map })

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

final_config = next(source_map) ~= nil and vim.tbl_deep_extend('force', final_config, { sourceMap = 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
7 changes: 6 additions & 1 deletion 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 Down

0 comments on commit a58b7c7

Please sign in to comment.