@@ -21,27 +21,39 @@ use std::path::PathBuf;
2121
2222pub fn install ( targets : Vec < String > , dirs : & ProjectDirs , is_offline : bool , asdeps : bool ) {
2323 let mut pacman_deps = HashSet :: new ( ) ;
24- let mut aur_packages = HashMap :: new ( ) ;
24+ let mut split_to_depth = HashMap :: new ( ) ;
25+ let mut split_to_pkgbase = HashMap :: new ( ) ;
26+ let mut split_to_version = HashMap :: new ( ) ;
2527 let alpm = pacman:: create_alpm ( ) ;
2628 for install_target in targets {
2729 resolve_dependencies (
2830 & install_target,
2931 & mut pacman_deps,
30- & mut aur_packages,
32+ & mut split_to_depth,
33+ & mut split_to_pkgbase,
34+ & mut split_to_version,
3135 0 ,
3236 & alpm,
3337 ) ;
3438 }
3539 pacman_deps. retain ( |name| !pacman:: is_package_installed ( & alpm, name) ) ;
36- show_install_summary ( & pacman_deps, & aur_packages) ;
37- for name in aur_packages. keys ( ) {
38- let dir = rua_files:: review_dir ( dirs, name) ;
39- fs:: create_dir_all ( & dir)
40- . unwrap_or_else ( |err| panic ! ( "Failed to create repository dir for {}, {}" , name, err) ) ;
41- reviewing:: review_repo ( & dir, name, dirs) ;
40+ show_install_summary ( & pacman_deps, & split_to_depth) ;
41+ for pkgbase in split_to_pkgbase. values ( ) . collect :: < HashSet < _ > > ( ) {
42+ let dir = rua_files:: review_dir ( dirs, pkgbase) ;
43+ fs:: create_dir_all ( & dir) . unwrap_or_else ( |err| {
44+ panic ! ( "Failed to create repository dir for {}, {}" , pkgbase, err)
45+ } ) ;
46+ reviewing:: review_repo ( & dir, pkgbase, dirs) ;
4247 }
4348 pacman:: ensure_pacman_packages_installed ( pacman_deps) ;
44- install_all ( dirs, aur_packages, is_offline, asdeps) ;
49+ install_all (
50+ dirs,
51+ split_to_depth,
52+ split_to_pkgbase,
53+ split_to_version,
54+ is_offline,
55+ asdeps,
56+ ) ;
4557}
4658
4759fn show_install_summary ( pacman_deps : & HashSet < String > , aur_packages : & HashMap < String , i32 > ) {
@@ -69,14 +81,42 @@ fn show_install_summary(pacman_deps: &HashSet<String>, aur_packages: &HashMap<St
6981 }
7082}
7183
72- fn install_all ( dirs : & ProjectDirs , packages : HashMap < String , i32 > , offline : bool , asdeps : bool ) {
73- let mut packages = packages. iter ( ) . collect :: < Vec < _ > > ( ) ;
74- packages. sort_by_key ( |pair| -* pair. 1 ) ;
75- for ( depth, packages) in & packages. iter ( ) . group_by ( |pair| * pair. 1 ) {
76- let packages: Vec < _ > = packages. map ( |pair| pair. 0 ) . collect ( ) ;
77- for name in & packages {
78- let review_dir = rua_files:: review_dir ( dirs, name) ;
79- let build_dir = rua_files:: build_dir ( dirs, name) ;
84+ fn install_all (
85+ dirs : & ProjectDirs ,
86+ split_to_depth : HashMap < String , i32 > ,
87+ split_to_pkgbase : HashMap < String , String > ,
88+ split_to_version : HashMap < String , String > ,
89+ offline : bool ,
90+ asdeps : bool ,
91+ ) {
92+ let archive_whitelist = split_to_version
93+ . into_iter ( )
94+ . map ( |pair| format ! ( "{}-{}" , pair. 0 , pair. 1 ) )
95+ . collect :: < Vec < _ > > ( ) ;
96+ trace ! ( "All expected archive files: {:?}" , archive_whitelist) ;
97+ // get a list of (pkgbase, depth)
98+ let packages = split_to_pkgbase. iter ( ) . map ( |( split, pkgbase) | {
99+ let depth = split_to_depth
100+ . get ( split)
101+ . expect ( "Internal error: split package doesn't have recursive depth" ) ;
102+ ( pkgbase. to_string ( ) , * depth, split. to_string ( ) )
103+ } ) ;
104+ // sort pairs in descending depth order
105+ let packages = packages. sorted_by_key ( |( _pkgbase, depth, _split) | -depth) ;
106+ // Note that a pkgbase can appear at multiple depths because
107+ // multiple split pkgnames can be at multiple depths.
108+ // In this case, we only take the first occurrence of pkgbase,
109+ // which would be the maximum depth because of sort order.
110+ // We only take one occurrence because we want the package to only be built once.
111+ let packages: Vec < ( String , i32 , String ) > = packages
112+ . unique_by ( |( pkgbase, _depth, _split) | pkgbase. to_string ( ) )
113+ . collect :: < Vec < _ > > ( ) ;
114+ // once we have a collection of pkgname-s and their depth, proceed straightforwardly.
115+ for ( depth, packages) in & packages. iter ( ) . group_by ( |( _pkgbase, depth, _split) | * depth) {
116+ let packages = packages. collect :: < Vec < & ( String , i32 , String ) > > ( ) ;
117+ for ( pkgbase, _depth, _split) in & packages {
118+ let review_dir = rua_files:: review_dir ( dirs, pkgbase) ;
119+ let build_dir = rua_files:: build_dir ( dirs, pkgbase) ;
80120 rm_rf:: force_remove_all ( & build_dir, true ) . expect ( "Failed to remove old build dir" ) ;
81121 std:: fs:: create_dir_all ( & build_dir) . expect ( "Failed to create build dir" ) ;
82122 fs_extra:: copy_items (
@@ -92,31 +132,35 @@ fn install_all(dirs: &ProjectDirs, packages: HashMap<String, i32>, offline: bool
92132 offline,
93133 ) ;
94134 }
95- for name in & packages {
96- check_tars_and_move ( name , dirs) ;
135+ for ( pkgbase , _depth , _split ) in & packages {
136+ check_tars_and_move ( pkgbase , dirs, & archive_whitelist ) ;
97137 }
138+ // This relation between split_name and the archive file is not actually correct here.
139+ // Instead, all archive files of some group will be bound to one split name only here.
140+ // This is probably still good enough for install verification though --
141+ // and we only use this relation for this purpose. Feel free to improve, if you want...
98142 let mut files_to_install: Vec < ( String , PathBuf ) > = Vec :: new ( ) ;
99- for name in & packages {
100- let checked_tars = rua_files:: checked_tars_dir ( dirs, & name ) ;
143+ for ( pkgbase , _depth , split ) in & packages {
144+ let checked_tars = rua_files:: checked_tars_dir ( dirs, & pkgbase ) ;
101145 let read_dir_iterator = fs:: read_dir ( checked_tars) . unwrap_or_else ( |e| {
102146 panic ! (
103147 "Failed to read 'checked_tars' directory for {}, {}" ,
104- name , e
148+ pkgbase , e
105149 )
106150 } ) ;
151+
107152 for file in read_dir_iterator {
108153 files_to_install. push ( (
109- name. to_string ( ) ,
110- file. expect ( "Failed to open file for tar_check analysis" )
111- . path ( ) ,
154+ split. to_string ( ) ,
155+ file. expect ( "Failed to access checked_tars dir" ) . path ( ) ,
112156 ) ) ;
113157 }
114158 }
115159 pacman:: ensure_aur_packages_installed ( files_to_install, asdeps || depth > 0 ) ;
116160 }
117161}
118162
119- pub fn check_tars_and_move ( name : & str , dirs : & ProjectDirs ) {
163+ pub fn check_tars_and_move ( name : & str , dirs : & ProjectDirs , archive_whitelist : & [ String ] ) {
120164 debug ! ( "{}:{} checking tars for package {}" , file!( ) , line!( ) , name) ;
121165 let build_dir = rua_files:: build_dir ( dirs, name) ;
122166 let dir_items: ReadDir = build_dir. read_dir ( ) . unwrap_or_else ( |err| {
@@ -125,14 +169,23 @@ pub fn check_tars_and_move(name: &str, dirs: &ProjectDirs) {
125169 & build_dir, err
126170 )
127171 } ) ;
128- let checked_files = dir_items. flat_map ( |file| {
129- tar_check:: tar_check (
130- & file
131- . expect ( "Failed to open file for tar_check analysis" )
132- . path ( ) ,
133- )
134- } ) ;
135- debug ! ( "all package (tar) files checked, moving them" , ) ;
172+ let dir_items = dir_items. map ( |f| f. expect ( "Failed to open file for tar_check analysis" ) ) ;
173+ let dir_items = dir_items
174+ . filter ( |file| {
175+ let file_name = file. file_name ( ) ;
176+ let file_name = file_name
177+ . to_str ( )
178+ . expect ( "Non-UTF8 characters in tar file name" ) ;
179+ archive_whitelist
180+ . iter ( )
181+ . any ( |prefix| file_name. starts_with ( prefix) )
182+ } )
183+ . collect :: < Vec < _ > > ( ) ;
184+ trace ! ( "Files filtered for tar checking: {:?}" , & dir_items) ;
185+ for file in dir_items. iter ( ) {
186+ tar_check:: tar_check ( & file. path ( ) )
187+ }
188+ debug ! ( "all package (tar) files checked, moving them" ) ;
136189 let checked_tars_dir = rua_files:: checked_tars_dir ( dirs, name) ;
137190 rm_rf:: force_remove_all ( & checked_tars_dir, true ) . unwrap_or_else ( |err| {
138191 panic ! (
@@ -147,12 +200,12 @@ pub fn check_tars_and_move(name: &str, dirs: &ProjectDirs) {
147200 ) ;
148201 } ) ;
149202
150- for file in checked_files {
151- let file_name = file. file_name ( ) . expect ( "Failed to parse package tar name" ) ;
203+ for file in dir_items {
204+ let file_name = file. file_name ( ) ;
152205 let file_name = file_name
153206 . to_str ( )
154207 . expect ( "Non-UTF8 characters in tar file name" ) ;
155- fs:: rename ( & file, checked_tars_dir. join ( file_name) ) . unwrap_or_else ( |e| {
208+ fs:: rename ( & file. path ( ) , checked_tars_dir. join ( file_name) ) . unwrap_or_else ( |e| {
156209 panic ! (
157210 "Failed to move {:?} (build artifact) to {:?}, {}" ,
158211 & file, & checked_tars_dir, e,
@@ -162,32 +215,50 @@ pub fn check_tars_and_move(name: &str, dirs: &ProjectDirs) {
162215}
163216
164217/// Check that the package name is easy to work with in shell
165- fn check_package_name ( name : & str ) {
218+ fn clean_and_check_package_name ( name : & str ) -> String {
219+ lazy_static ! {
220+ static ref CLEANUP : Regex = Regex :: new( r"(=.*|>.*|<.*)" ) . unwrap_or_else( |err| panic!(
221+ "{}:{} Failed to parse regexp, {}" ,
222+ file!( ) ,
223+ line!( ) ,
224+ err
225+ ) ) ;
226+ }
227+ let name: String = CLEANUP . replace_all ( name, "" ) . to_string ( ) ;
166228 lazy_static ! {
167- static ref NAME_REGEX : Regex = Regex :: new( r"[a-zA-Z][a-zA-Z._-]*" )
168- . unwrap_or_else( |_| panic!( "{}:{} Failed to parse regexp" , file!( ) , line!( ) ) ) ;
229+ static ref NAME_REGEX : Regex = Regex :: new( r"^[a-zA-Z][a-zA-Z0-9._+-]*$" ) . unwrap_or_else(
230+ |err| panic!( "{}:{} Failed to parse regexp, {}" , file!( ) , line!( ) , err)
231+ ) ;
169232 }
170- if !NAME_REGEX . is_match ( name) {
233+ if !NAME_REGEX . is_match ( & name) {
171234 eprintln ! ( "Unexpected package name {}" , name) ;
172235 std:: process:: exit ( 1 )
173236 }
237+ name
174238}
175239
240+ /// Resolve dependencies recursively.
241+ /// "split_name" is the `pkgname` in PKGBUILD terminology. It's called "split" to avoid
242+ /// ambiguity of "package name" meaning.
176243fn resolve_dependencies (
177- name : & str ,
244+ split_name : & str ,
178245 pacman_deps : & mut HashSet < String > ,
179- aur_packages : & mut HashMap < String , i32 > ,
246+ split_to_depth : & mut HashMap < String , i32 > ,
247+ split_to_pkgbase : & mut HashMap < String , String > ,
248+ split_to_version : & mut HashMap < String , String > ,
180249 depth : i32 ,
181250 alpm : & Alpm ,
182251) {
183- check_package_name ( & name ) ;
184- if let Some ( old_depth) = aur_packages . get ( name ) {
252+ let split_name = clean_and_check_package_name ( & split_name ) ;
253+ if let Some ( old_depth) = split_to_depth . get ( & split_name ) {
185254 let old_depth = * old_depth;
186- aur_packages . insert ( name . to_owned ( ) , cmp:: max ( depth + 1 , old_depth) ) ;
187- info ! ( "Skipping already resolved package {}" , name ) ;
255+ split_to_depth . insert ( split_name . to_owned ( ) , cmp:: max ( depth + 1 , old_depth) ) ;
256+ info ! ( "Skipping already resolved package {}" , split_name ) ;
188257 } else {
189- aur_packages. insert ( name. to_owned ( ) , depth) ;
190- let info = raur_info ( & name) ;
258+ split_to_depth. insert ( split_name. to_owned ( ) , depth) ;
259+ let info = raur_info ( & split_name) ;
260+ split_to_pkgbase. insert ( split_name. to_string ( ) , info. package_base ) ;
261+ split_to_version. insert ( split_name. to_string ( ) , info. version ) ;
191262 let deps = info
192263 . depends
193264 . iter ( )
@@ -199,9 +270,17 @@ fn resolve_dependencies(
199270 } else if !pacman:: is_package_installable ( alpm, & dep) {
200271 info ! (
201272 "{} depends on AUR package {}. Trying to resolve it..." ,
202- name, & dep
273+ split_name, & dep
274+ ) ;
275+ resolve_dependencies (
276+ & dep,
277+ pacman_deps,
278+ split_to_depth,
279+ split_to_pkgbase,
280+ split_to_version,
281+ depth + 1 ,
282+ alpm,
203283 ) ;
204- resolve_dependencies ( & dep, pacman_deps, aur_packages, depth + 1 , alpm) ;
205284 } else {
206285 pacman_deps. insert ( dep. to_owned ( ) ) ;
207286 }
0 commit comments