@@ -36,6 +36,9 @@ impl Display for UnixPackageManager {
3636 }
3737}
3838
39+ #[ cfg( target_os = "linux" ) ]
40+ impl super :: caching:: Cacher for UnixPackageManager { }
41+
3942impl 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+ }
0 commit comments