@@ -36,6 +36,9 @@ impl Display for UnixPackageManager {
3636 }
3737}
3838
39+ #[ cfg( target_os = "linux" ) ]
40+ impl crate :: downloader:: 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" ) ]
@@ -114,7 +121,7 @@ impl PackageManager for UnixPackageManager {
114121 }
115122 }
116123
117- fn install_package (
124+ async fn install_package (
118125 & self ,
119126 package_name : & str ,
120127 version : Option < & Version > ,
@@ -134,13 +141,37 @@ 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" )
146+ . arg ( self . as_str ( ) )
147+ . args ( args)
148+ . arg ( package_id. as_str ( ) )
149+ . output ( ) ?
150+ } else {
151+ Command :: new ( self . as_str ( ) )
152+ . args ( args)
153+ . arg ( package_id. as_str ( ) )
154+ . output ( ) ?
155+ } ;
141156 if output. status . success ( ) {
142157 Ok ( ( ) )
143158 } else {
159+ #[ cfg( target_os = "linux" ) ]
160+ if matches ! ( self , UnixPackageManager :: Apt )
161+ && let Some ( version) = version
162+ {
163+ use crate :: downloader:: caching:: Cacher ;
164+
165+ log:: info!(
166+ "trying to install from official LLVM PPA repository (for Debian-based `apt` package manager)"
167+ ) ;
168+ return llvm_apt_install:: install_llvm_via_apt (
169+ Self :: get_cache_dir ( ) . as_path ( ) ,
170+ version. major . to_string ( ) ,
171+ package_id. as_str ( ) ,
172+ )
173+ . await ;
174+ }
144175 Err ( PackageManagerError :: InstallationError {
145176 manager : self . as_str ( ) . to_string ( ) ,
146177 package : package_id,
@@ -181,3 +212,75 @@ impl PackageManager for UnixPackageManager {
181212 }
182213 }
183214}
215+
216+ #[ cfg( target_os = "linux" ) ]
217+ mod llvm_apt_install {
218+ use crate :: downloader:: {
219+ chmod_file, download,
220+ native_packages:: { PackageManagerError , unix:: UnixPackageManager } ,
221+ } ;
222+ use std:: { path:: Path , process:: Command } ;
223+ use url:: Url ;
224+
225+ const LLVM_INSTALL_SCRIPT_URL : & str = "https://apt.llvm.org/llvm.sh" ;
226+
227+ /// Installs the official LLVM APT repository and its GPG key.
228+ ///
229+ /// This is required to install specific versions of clang tools on Debian-based distributions using `apt`.}
230+ pub async fn install_llvm_via_apt (
231+ cache_path : & Path ,
232+ ver_major : String ,
233+ package_name : & str ,
234+ ) -> Result < ( ) , PackageManagerError > {
235+ let download_path = cache_path. join ( "llvm_apt_install.sh" ) ;
236+ if !download_path. exists ( ) {
237+ log:: info!(
238+ "Downloading LLVM APT repository installation script from {LLVM_INSTALL_SCRIPT_URL}"
239+ ) ;
240+ download (
241+ & Url :: parse ( LLVM_INSTALL_SCRIPT_URL ) ?,
242+ & download_path,
243+ 60 * 2 ,
244+ )
245+ . await ?;
246+ chmod_file ( & download_path, Some ( 0o111 ) ) ?;
247+ }
248+ let has_sudo = UnixPackageManager :: has_sudo ( ) ;
249+
250+ let output = if has_sudo {
251+ Command :: new ( "sudo" )
252+ . arg ( "bash" )
253+ . arg ( download_path. as_os_str ( ) )
254+ . arg ( ver_major)
255+ . output ( ) ?
256+ } else {
257+ Command :: new ( "bash" )
258+ . arg ( download_path. as_os_str ( ) )
259+ . arg ( ver_major)
260+ . output ( ) ?
261+ } ;
262+ if !output. status . success ( ) {
263+ return Err ( PackageManagerError :: LlvmPpaError (
264+ String :: from_utf8_lossy ( & output. stderr ) . to_string ( ) ,
265+ ) ) ;
266+ }
267+ let output = if has_sudo {
268+ Command :: new ( "sudo" )
269+ . arg ( "apt" )
270+ . args ( [ "install" , "-y" , package_name] )
271+ . output ( )
272+ } else {
273+ Command :: new ( "apt" )
274+ . args ( [ "install" , "-y" , package_name] )
275+ . output ( )
276+ } ?;
277+ if !output. status . success ( ) {
278+ return Err ( PackageManagerError :: InstallationError {
279+ manager : "apt (with LLVM PPA)" . to_string ( ) ,
280+ package : package_name. to_string ( ) ,
281+ stderr : String :: from_utf8_lossy ( & output. stderr ) . to_string ( ) ,
282+ } ) ;
283+ }
284+ Ok ( ( ) )
285+ }
286+ }
0 commit comments