debugger/setup/adapters/
codelldb.rs1use crate::common::{Error, Result};
6use crate::setup::installer::{
7 adapters_dir, arch_str, download_file, ensure_adapters_dir, extract_zip,
8 get_github_release, make_executable, platform_str, read_version_file,
9 write_version_file, InstallMethod, InstallOptions, InstallResult, InstallStatus, Installer,
10};
11use crate::setup::registry::{DebuggerInfo, Platform};
12use crate::setup::verifier::{verify_dap_adapter, VerifyResult};
13use async_trait::async_trait;
14
15static INFO: DebuggerInfo = DebuggerInfo {
16 id: "codelldb",
17 name: "CodeLLDB",
18 languages: &["c", "cpp", "rust"],
19 platforms: &[Platform::Linux, Platform::MacOS, Platform::Windows],
20 description: "Feature-rich LLDB-based debugger",
21 primary: false,
22};
23
24const GITHUB_REPO: &str = "vadimcn/codelldb";
25
26pub struct CodeLldbInstaller;
27
28#[async_trait]
29impl Installer for CodeLldbInstaller {
30 fn info(&self) -> &DebuggerInfo {
31 &INFO
32 }
33
34 async fn status(&self) -> Result<InstallStatus> {
35 let adapter_dir = adapters_dir().join("codelldb");
36 let binary_path = adapter_dir.join("extension").join("adapter").join(binary_name());
37
38 if binary_path.exists() {
39 let version = read_version_file(&adapter_dir);
40 return Ok(InstallStatus::Installed {
41 path: binary_path,
42 version,
43 });
44 }
45
46 if let Ok(path) = which::which("codelldb") {
48 return Ok(InstallStatus::Installed {
49 path,
50 version: None,
51 });
52 }
53
54 Ok(InstallStatus::NotInstalled)
55 }
56
57 async fn best_method(&self) -> Result<InstallMethod> {
58 Ok(InstallMethod::GitHubRelease {
60 repo: GITHUB_REPO.to_string(),
61 asset_pattern: format!("codelldb-{}-{}.vsix", arch_str(), platform_str()),
62 })
63 }
64
65 async fn install(&self, opts: InstallOptions) -> Result<InstallResult> {
66 install_from_github(&opts).await
67 }
68
69 async fn uninstall(&self) -> Result<()> {
70 let adapter_dir = adapters_dir().join("codelldb");
71 if adapter_dir.exists() {
72 std::fs::remove_dir_all(&adapter_dir)?;
73 println!("Removed {}", adapter_dir.display());
74 } else {
75 println!("CodeLLDB is not installed");
76 }
77 Ok(())
78 }
79
80 async fn verify(&self) -> Result<VerifyResult> {
81 let status = self.status().await?;
82
83 match status {
84 InstallStatus::Installed { path, .. } => {
85 verify_dap_adapter(&path, &[]).await
87 }
88 InstallStatus::Broken { reason, .. } => Ok(VerifyResult {
89 success: false,
90 capabilities: None,
91 error: Some(reason),
92 }),
93 InstallStatus::NotInstalled => Ok(VerifyResult {
94 success: false,
95 capabilities: None,
96 error: Some("Not installed".to_string()),
97 }),
98 }
99 }
100}
101
102fn binary_name() -> &'static str {
103 if cfg!(windows) {
104 "codelldb.exe"
105 } else {
106 "codelldb"
107 }
108}
109
110fn get_asset_pattern() -> Vec<String> {
111 let platform = platform_str();
112 let arch = arch_str();
113
114 let codelldb_arch = match arch {
116 "x86_64" => "x86_64",
117 "aarch64" => "aarch64",
118 _ => arch,
119 };
120
121 let codelldb_platform = match platform {
123 "darwin" => "darwin",
124 "linux" => "linux",
125 "windows" => "windows",
126 _ => platform,
127 };
128
129 vec![
130 format!("codelldb-{}-{}.vsix", codelldb_arch, codelldb_platform),
131 format!("codelldb-{}-{}-*.vsix", codelldb_arch, codelldb_platform),
133 ]
134}
135
136async fn install_from_github(opts: &InstallOptions) -> Result<InstallResult> {
137 println!("Checking for existing installation... not found");
138 println!("Finding latest CodeLLDB release...");
139
140 let release = get_github_release(GITHUB_REPO, opts.version.as_deref()).await?;
141 let version = release.tag_name.trim_start_matches('v').to_string();
142 println!("Found version: {}", version);
143
144 let patterns = get_asset_pattern();
146 let asset = release
147 .find_asset(&patterns.iter().map(|s| s.as_str()).collect::<Vec<_>>())
148 .ok_or_else(|| {
149 Error::Internal(format!(
150 "No CodeLLDB release found for {} {}. Available assets: {:?}",
151 arch_str(),
152 platform_str(),
153 release.assets.iter().map(|a| &a.name).collect::<Vec<_>>()
154 ))
155 })?;
156
157 let temp_dir = tempfile::tempdir()?;
159 let archive_path = temp_dir.path().join(&asset.name);
160
161 println!(
162 "Downloading {}... {:.1} MB",
163 asset.name,
164 asset.size as f64 / 1_000_000.0
165 );
166 download_file(&asset.browser_download_url, &archive_path).await?;
167
168 println!("Extracting...");
169
170 let adapter_dir = ensure_adapters_dir()?.join("codelldb");
172 if adapter_dir.exists() {
173 std::fs::remove_dir_all(&adapter_dir)?;
174 }
175 std::fs::create_dir_all(&adapter_dir)?;
176
177 extract_zip(&archive_path, &adapter_dir)?;
179
180 let binary_path = adapter_dir.join("extension").join("adapter").join(binary_name());
182 if !binary_path.exists() {
183 return Err(Error::Internal(format!(
184 "codelldb binary not found at expected location: {}",
185 binary_path.display()
186 )));
187 }
188 make_executable(&binary_path)?;
189
190 #[cfg(unix)]
192 {
193 let lib_path = adapter_dir.join("extension").join("adapter");
194 for entry in std::fs::read_dir(&lib_path)? {
195 if let Ok(entry) = entry {
196 let path = entry.path();
197 if path.extension().map(|e| e == "so" || e == "dylib").unwrap_or(false) {
198 make_executable(&path)?;
199 }
200 }
201 }
202
203 let lldb_dir = adapter_dir.join("extension").join("lldb");
205 if lldb_dir.exists() {
206 for subdir in &["bin", "lib"] {
207 let dir = lldb_dir.join(subdir);
208 if dir.exists() {
209 if let Ok(entries) = std::fs::read_dir(&dir) {
210 for entry in entries {
211 if let Ok(entry) = entry {
212 let path = entry.path();
213 if path.is_file() {
214 if let Err(e) = make_executable(&path) {
216 eprintln!(
217 "Warning: could not make {} executable: {}",
218 path.display(),
219 e
220 );
221 }
222 }
223 }
224 }
225 }
226 }
227 }
228 }
229 }
230
231 write_version_file(&adapter_dir, &version)?;
233
234 println!("Setting permissions... done");
235 println!("Verifying installation...");
236
237 Ok(InstallResult {
238 path: binary_path,
239 version: Some(version),
240 args: Vec::new(),
241 })
242}