Skip to content

Commit 3c9fa8a

Browse files
committed
add fallback to LLVM install script (for apt)
and conditionally use `sudo`
1 parent d17eab7 commit 3c9fa8a

File tree

4 files changed

+119
-13
lines changed

4 files changed

+119
-13
lines changed

clang-installer/src/downloader/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,27 @@ async fn download(url: &Url, cache_path: &Path, timeout: u64) -> Result<(), Down
7272
Ok(())
7373
}
7474

75+
/// On Unix-like systems, this changes the permissions of the file at `path`.
76+
///
77+
/// If `mode` is `Some`, then the permissions will be set to the bitwise OR of
78+
/// the existing permissions and given `mode`.
79+
/// If `mode` is `None`, then the permissions will be set to `0o755`.
80+
#[cfg(unix)]
81+
fn chmod_file(path: &Path, mode: Option<u32>) -> std::io::Result<()> {
82+
// Make the extracted binary executable on Unix-like systems.
83+
use std::os::unix::fs::PermissionsExt;
84+
let out = fs::OpenOptions::new().write(true).open(&path)?;
85+
let mut perms = out.metadata()?.permissions();
86+
match mode {
87+
Some(mode) => {
88+
let prev = perms.mode();
89+
perms.set_mode(prev | mode);
90+
}
91+
None => perms.set_mode(0o755),
92+
}
93+
out.set_permissions(perms)
94+
}
95+
7596
#[cfg(test)]
7697
mod tests {
7798
use crate::DownloadError;

clang-installer/src/downloader/native_packages/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ pub enum PackageManagerError {
2020
package: String,
2121
stderr: String,
2222
},
23+
#[cfg(target_os = "linux")]
24+
#[error("Failed to add LLVM PPA repository (for `apt`): {0}")]
25+
LlvmPpaError(String),
2326
}
2427

2528
pub trait PackageManager {
@@ -97,7 +100,7 @@ pub fn try_install_package(
97100
log::info!(
98101
"Successfully installed {tool} v{min_version} using {mgr} package manager."
99102
);
100-
let path = tool.get_exe_path(&RequestedVersion::SystemDefault)?;
103+
let path = tool.get_exe_path(&RequestedVersion::Requirement(version_req.clone()))?;
101104
let version = tool.capture_version(&path)?;
102105
if version_req.matches(&version) {
103106
log::info!(

clang-installer/src/downloader/native_packages/unix.rs

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ impl Display for UnixPackageManager {
3636
}
3737
}
3838

39+
#[cfg(target_os = "linux")]
40+
impl super::caching::Cacher for UnixPackageManager {}
41+
3942
impl UnixPackageManager {
4043
fn as_str(&self) -> &'static str {
4144
match self {
@@ -49,6 +52,10 @@ impl UnixPackageManager {
4952
}
5053
}
5154

55+
fn has_sudo() -> bool {
56+
which::which("sudo").is_ok()
57+
}
58+
5259
fn pkg_name_with_version(&self, package_name: &str, version: Option<&Version>) -> String {
5360
match self {
5461
#[cfg(target_os = "linux")]
@@ -134,13 +141,30 @@ impl PackageManager for UnixPackageManager {
134141
args.push("install");
135142
}
136143
}
137-
let output = Command::new(self.as_str())
138-
.args(args)
139-
.arg(package_id.as_str())
140-
.output()?;
144+
let output = if self.has_sudo() {
145+
Command::new("sudo").arg(self.as_str()).args(args)
146+
} else {
147+
Command::new(self.as_str()).args(args)
148+
}
149+
.arg(package_id.as_str())
150+
.output()?;
141151
if output.status.success() {
142152
Ok(())
143153
} else {
154+
#[cfg(target_os = "linux")]
155+
if matches!(self, UnixPackageManager::Apt)
156+
&& let Some(version) = version
157+
{
158+
log::info!(
159+
"trying to install from official LLVM PPA repository (for Debian-based `apt` package manager)"
160+
);
161+
return llvm_apt_install::install_llvm_via_apt(
162+
self.get_cache_dir().as_path(),
163+
version.major.to_string(),
164+
package_id.as_str(),
165+
)
166+
.await;
167+
}
144168
Err(PackageManagerError::InstallationError {
145169
manager: self.as_str().to_string(),
146170
package: package_id,
@@ -181,3 +205,68 @@ impl PackageManager for UnixPackageManager {
181205
}
182206
}
183207
}
208+
209+
#[cfg(target_os = "linux")]
210+
mod llvm_apt_install {
211+
use super::super::{chmod, download};
212+
use std::{path::Path, process::Command};
213+
use url::Url;
214+
215+
const LLVM_INSTALL_SCRIPT_URL: &str = "https://apt.llvm.org/llvm.sh";
216+
217+
/// Installs the official LLVM APT repository and its GPG key.
218+
///
219+
/// This is required to install specific versions of clang tools on Debian-based distributions using `apt`.}
220+
pub async fn install_llvm_via_apt(
221+
cache_path: &Path,
222+
ver_major: String,
223+
package_name: &str,
224+
) -> Result<(), PackageManagerError> {
225+
let download_path = self.get_cache_dir().join("llvm_apt_install.sh");
226+
if !download_path.exists() {
227+
log::info!(
228+
"Downloading LLVM APT repository installation script from {LLVM_INSTALL_SCRIPT_URL}"
229+
);
230+
download(
231+
&Url::parse(LLVM_INSTALL_SCRIPT_URL)?,
232+
&download_path,
233+
60 * 2,
234+
)
235+
.await?;
236+
chmod_file(&download_path, Some(0o111))?;
237+
}
238+
let has_sudo = self.has_sudo();
239+
240+
let output = if has_sudo {
241+
Command::new("sudo").arg("bash")
242+
} else {
243+
Command::new("bash")
244+
}
245+
.arg(download_path.as_os_str())
246+
.arg(ver_major)
247+
.output()?;
248+
if !output.status.success() {
249+
return Err(PackageManagerError::LlvmPpaError(
250+
String::from_utf8_lossy(&output.stderr).to_string(),
251+
));
252+
}
253+
let output = if has_sudo {
254+
Command::new("sudo")
255+
.arg("apt")
256+
.args(["install", "-y", package_name])
257+
.output()
258+
} else {
259+
Command::new("apt")
260+
.args(["install", "-y", package_name])
261+
.output()
262+
}?;
263+
if !output.status.success() {
264+
return Err(PackageManagerError::InstallationError {
265+
manager: "apt (with LLVM PPA)".to_string(),
266+
package: package_name.to_string(),
267+
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
268+
});
269+
}
270+
Ok(())
271+
}
272+
}

clang-installer/src/downloader/static_dist.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,7 @@ impl StaticDistDownloader {
151151
log::info!("Downloading static binary for {tool} version {ver_str} from {url}");
152152
download(&url, &download_path, 60 * 2).await?;
153153
#[cfg(unix)]
154-
{
155-
// Make the extracted binary executable on Unix-like systems.
156-
use std::os::unix::fs::PermissionsExt;
157-
let out = fs::OpenOptions::new().write(true).open(&download_path)?;
158-
let mut perms = out.metadata()?.permissions();
159-
perms.set_mode(0o755);
160-
out.set_permissions(perms)?;
161-
}
154+
super::chmod_file(&download_path, None)?;
162155
}
163156
let sha512_cache_path = cache_path
164157
.join("static_dist")

0 commit comments

Comments
 (0)