1use crate::actions::{Actions, AurPackage, DepMissing, Missing, RepoPackage, Unneeded};
2use crate::base::Base;
3use crate::cb::{Group, GroupCB, IsDevelCb, ProviderCB};
4use crate::pkgbuild::PkgbuildRepo;
5use crate::satisfies::{satisfies_provide, Satisfies};
6use crate::{AurBase, Error, Pkgbuild, PkgbuildPackages};
7
8use std::collections::HashSet;
9
10use alpm::{Alpm, Dep, Depend, Version};
11use alpm_utils::{AsTarg, DbListExt, Targ};
12use bitflags::bitflags;
13use log::Level::Debug;
14use log::{debug, log_enabled};
15use raur::{ArcPackage, Cache, Raur, SearchBy};
16
17enum RepoSource {
22 Repo,
23 Pkgbuild,
24 Aur,
25 Unspecified,
26 Missing,
27}
28
29#[derive(Default)]
30struct Targets<'t, 'a> {
31 repo: Vec<(Targ<'t>, &'a alpm::Package)>,
32 pkgbuild: Vec<Targ<'t>>,
33 aur: Vec<&'t str>,
34}
35
36bitflags! {
37 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
39 pub struct Flags: u32 {
40 const NO_DEPS = 1 << 2;
42 const NO_DEP_VERSION = 1 << 3;
44 const TARGET_PROVIDES = 1 << 4;
46 const NON_TARGET_PROVIDES = 1 << 5;
48 const MISSING_PROVIDES = 1 << 6;
50 const PROVIDES = 1 << 7;
52 const CALCULATE_MAKE = 1 << 8;
54 const CHECK_DEPENDS = 1 << 10;
56 const NEEDED = 1 << 11;
58 const PKGBUILDS = 1 << 12;
60 const AUR = 1 << 13;
62 const REPO = 1 << 14;
64 const ENABLE_DOWNGRADE = 1 << 15;
66 const RESOLVE_SATISFIED_PKGBUILDS = 1 << 16;
68 }
69}
70
71impl Flags {
72 pub fn new() -> Self {
74 Flags::CALCULATE_MAKE
75 | Flags::TARGET_PROVIDES
76 | Flags::MISSING_PROVIDES
77 | Flags::CHECK_DEPENDS
78 | Flags::AUR
79 | Flags::REPO
80 | Flags::PKGBUILDS
81 }
82
83 pub fn aur_only() -> Self {
85 Flags::new() & !Flags::REPO
86 }
87}
88
89impl Default for Flags {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95#[derive(Debug)]
96#[allow(dead_code)]
97enum AurOrPkgbuild<'a> {
98 Aur(&'a raur::Package),
99 Pkgbuild(&'a str, &'a srcinfo::Srcinfo, &'a srcinfo::Package),
100}
101
102impl<'a> AurOrPkgbuild<'a> {
103 fn pkgbase(&self) -> &str {
104 match self {
105 AurOrPkgbuild::Aur(pkg) => &pkg.package_base,
106 AurOrPkgbuild::Pkgbuild(_, base, _) => &base.base.pkgbase,
107 }
108 }
109
110 fn depends(&self, arch: &str, check_depends: bool) -> Vec<&str> {
111 match self {
112 AurOrPkgbuild::Aur(pkg) => {
113 let check = if check_depends {
114 Some(&pkg.check_depends)
115 } else {
116 None
117 };
118
119 pkg.make_depends
120 .iter()
121 .chain(check.into_iter().flatten())
122 .chain(&pkg.depends)
123 .map(|s| s.as_str())
124 .collect()
125 }
126 AurOrPkgbuild::Pkgbuild(_, src, pkg) => {
127 let base = &src.base;
128 let check = if check_depends {
129 Some(&base.checkdepends)
130 } else {
131 None
132 };
133
134 base.makedepends
135 .iter()
136 .chain(check.into_iter().flatten())
137 .chain(&src.pkg.depends)
138 .chain(&pkg.depends)
139 .filter(|d| d.arch.is_none() || d.arch.as_deref() == Some(arch))
140 .flat_map(|d| &d.vec)
141 .map(|d| d.as_str())
142 .collect()
143 }
144 }
145 }
146}
147
148#[derive(Debug)]
190pub struct Resolver<'a, 'b, H = raur::Handle> {
191 pub(crate) alpm: &'a Alpm,
192 pub(crate) repos: Vec<PkgbuildRepo<'a>>,
193 pub(crate) resolved: HashSet<String>,
194 pub(crate) cache: &'b mut Cache,
195 pub(crate) stack: Vec<DepMissing>,
196 pub(crate) raur: &'b H,
197 pub(crate) actions: Actions<'a>,
198 pub(crate) seen: HashSet<String>,
199 pub(crate) seen_target: HashSet<String>,
200 pub(crate) flags: Flags,
201 pub(crate) aur_namespace: Option<String>,
202 pub(crate) provider_callback: ProviderCB,
203 pub(crate) group_callback: GroupCB<'a>,
204 pub(crate) is_devel: IsDevelCb,
205}
206
207impl<'a, 'b, E: std::error::Error + Sync + Send + 'static, H: Raur<Err = E> + Sync>
208 Resolver<'a, 'b, H>
209{
210 pub fn new(alpm: &'a Alpm, cache: &'b mut Cache, raur: &'b H, flags: Flags) -> Self {
212 let actions = Actions {
213 alpm,
214 missing: Vec::new(),
215 unneeded: Vec::new(),
216 build: Vec::new(),
217 install: Vec::new(),
218 };
219
220 Resolver {
221 alpm,
222 repos: Vec::new(),
223 resolved: HashSet::new(),
224 cache,
225 stack: Vec::new(),
226 actions,
227 raur,
228 flags,
229 seen: HashSet::new(),
230 seen_target: HashSet::new(),
231 aur_namespace: None,
232 provider_callback: Default::default(),
233 group_callback: Default::default(),
234 is_devel: Default::default(),
235 }
236 }
237
238 pub fn aur_namespace(mut self, enable: bool) -> Self {
240 if enable {
241 self.aur_namespace = Some("aur".to_string());
242 }
243 self
244 }
245
246 pub fn custom_aur_namespace(mut self, name: Option<String>) -> Self {
248 self.aur_namespace = name;
249 self
250 }
251
252 pub fn pkgbuild_repos(mut self, repos: Vec<PkgbuildRepo<'a>>) -> Self {
254 self.repos = repos;
255 self
256 }
257
258 pub fn get_cache(&self) -> &Cache {
260 self.cache
261 }
262
263 pub fn get_cache_mut(&mut self) -> &mut Cache {
265 self.cache
266 }
267
268 pub async fn resolve_targets<T: AsTarg>(self, pkgs: &[T]) -> Result<Actions<'a>, Error> {
270 self.resolve(pkgs, &[], true).await
271 }
272
273 pub async fn resolve_depends<T: AsRef<str>>(
275 self,
276 deps: &[T],
277 make_deps: &[T],
278 ) -> Result<Actions<'a>, Error> {
279 self.resolve(deps, make_deps, false).await
280 }
281
282 fn where_is_target(&self, targ: Targ) -> RepoSource {
284 if let Some(repo) = targ.repo {
285 if self.alpm.syncdbs().into_iter().any(|db| db.name() == repo) {
286 RepoSource::Repo
287 } else if self.repos.iter().any(|r| r.name == repo) {
288 RepoSource::Pkgbuild
289 } else if Some(repo) == self.aur_namespace.as_deref() {
290 RepoSource::Aur
291 } else {
292 RepoSource::Missing
293 }
294 } else {
295 RepoSource::Unspecified
296 }
297 }
298
299 fn split_targets<'t, T: AsTarg>(
300 &mut self,
301 deps: &'t [T],
302 make_deps: &'t [T],
303 is_target: bool,
304 ) -> Targets<'t, 'a> {
305 let mut targets = Targets::default();
306
307 let use_repo = self.flags.contains(Flags::REPO) || !is_target;
308 let use_pkgbuild = self.flags.contains(Flags::PKGBUILDS) || !is_target;
309 let use_aur = self.flags.contains(Flags::AUR) || !is_target;
310
311 'deps: for targ in deps.iter().chain(make_deps) {
312 let targ = targ.as_targ();
313 let dep = Depend::new(targ.to_string());
317 if !is_target && self.assume_installed(&dep) {
318 continue;
319 }
320
321 let source = self.where_is_target(targ);
322
323 if matches!(source, RepoSource::Repo | RepoSource::Unspecified) && use_repo {
324 if let Some(alpm_pkg) = self.find_repo_target_satisfier(targ) {
325 targets.repo.push((targ, alpm_pkg));
326 continue;
327 }
328
329 if is_target {
330 let groups = self
331 .alpm
332 .syncdbs()
333 .iter()
334 .filter(|db| targ.repo.is_none() || targ.repo.unwrap() == db.name())
335 .filter_map(|db| db.group(targ.pkg).map(|group| Group { db, group }).ok())
336 .collect::<Vec<_>>();
337 if !groups.is_empty() {
338 if let Some(f) = self.group_callback.get() {
339 for alpm_pkg in f(&groups) {
340 targets.repo.push((targ, alpm_pkg));
341 }
342 } else {
343 for group in groups {
344 for alpm_pkg in group.group.packages() {
345 targets.repo.push((targ, alpm_pkg));
346 }
347 }
348 }
349 continue;
350 }
351 }
352 }
353
354 if matches!(source, RepoSource::Pkgbuild | RepoSource::Unspecified) && use_pkgbuild {
355 for repo in &self.repos {
356 if targ.repo.is_some() && targ.repo != Some(repo.name) {
357 continue;
358 }
359
360 for base in &repo.pkgs {
361 if let Some(_satisfier) = base.which_satisfies_dep(
362 &Depend::new(targ.pkg),
363 self.flags.contains(Flags::NO_DEP_VERSION),
364 ) {
365 targets.pkgbuild.push(targ);
366 continue 'deps;
367 }
368 }
369 }
370 }
371
372 if matches!(source, RepoSource::Aur | RepoSource::Unspecified) && use_aur {
373 targets.aur.push(targ.pkg);
374 continue;
375 }
376
377 self.actions.missing.push(Missing {
378 dep: targ.to_string(),
379 stack: Vec::new(),
380 });
381 }
382
383 targets
384 }
385
386 async fn resolve<T: AsTarg>(
387 mut self,
388 deps: &[T],
389 make_deps: &[T],
390 is_target: bool,
391 ) -> Result<Actions<'a>, Error> {
392 let targets = self.split_targets(deps, make_deps, is_target);
393
394 let make = make_deps
395 .iter()
396 .map(|t| t.as_targ().pkg)
397 .collect::<HashSet<&str>>();
398
399 debug!("aur targets are {:?}", targets.aur);
400 debug!("pkgbuild targets are {:?}", targets.pkgbuild);
401
402 self.cache_aur_pkgs_recursive(&targets.aur, &targets.pkgbuild, true)
403 .await?;
404 self.resolved.clear();
405
406 debug!("Caching done, building tree");
407 debug!("cache: {:#?}", self.cache);
408
409 for (pkg, alpm_pkg) in targets.repo {
410 self.resolve_repo_target(pkg, alpm_pkg, &make, is_target)
411 }
412
413 for &pkg in &targets.pkgbuild {
414 self.resolve_pkgbuild_target(pkg, &make, is_target, &targets.aur)?;
415 }
416
417 for &aur_pkg in &targets.aur {
418 self.resolve_aur_target(aur_pkg, &make, is_target, &targets.aur)?;
419 }
420
421 if self.flags.contains(Flags::CALCULATE_MAKE) {
422 self.calculate_make();
423 }
424
425 Ok(self.actions)
426 }
427
428 fn resolve_aur_target(
429 &mut self,
430 aur_pkg: &str,
431 make: &HashSet<&str>,
432 is_target: bool,
433 targs: &[&str],
434 ) -> Result<(), Error> {
435 let dep = Depend::new(aur_pkg);
436 let localdb = self.alpm.localdb();
437
438 self.seen_target.insert(aur_pkg.to_string());
439
440 if self.should_skip_aur_pkg(&dep, is_target) {
441 return Ok(());
442 }
443
444 let pkg = if let Some(pkg) = self.select_satisfier_aur_cache(&dep, is_target) {
445 pkg.clone()
446 } else {
447 self.actions.missing.push(Missing {
448 dep: dep.to_string(),
449 stack: self.stack.clone(),
450 });
451 return Ok(());
452 };
453
454 if self.flags.contains(Flags::NEEDED) || !is_target {
455 let is_devel = self.is_devel.get().map(|f| f(aur_pkg)).unwrap_or(false);
456
457 if !is_devel {
458 if let Ok(local) = localdb.pkg(&*pkg.name) {
459 if local.version() >= Version::new(&*pkg.version) {
460 let unneeded =
461 Unneeded::new(aur_pkg.to_string(), local.version().to_string());
462 self.actions.unneeded.push(unneeded);
463 return Ok(());
464 }
465 }
466 }
467 }
468
469 let is_make = make.contains(&aur_pkg);
470 self.stack
471 .push(DepMissing::new(pkg.name.to_string(), aur_pkg.to_string()));
472 self.resolve_aur_pkg_deps(targs, AurOrPkgbuild::Aur(&pkg), is_make)?;
473 self.stack.pop().unwrap();
474
475 if self.actions.iter_aur_pkgs().any(|p| p.pkg.name == pkg.name) {
476 return Ok(());
477 }
478 if self
479 .actions
480 .iter_pkgbuilds()
481 .any(|p| p.1.pkg.pkgname == pkg.name)
482 {
483 return Ok(());
484 }
485
486 let p = AurPackage {
487 pkg: pkg.clone(),
488 make: false,
489 target: is_target,
490 };
491
492 self.push_aur_build(&pkg.package_base, p);
493 Ok(())
494 }
495
496 fn resolve_repo_target(
497 &mut self,
498 pkg: Targ,
499 alpm_pkg: &'a alpm::Package,
500 make: &HashSet<&str>,
501 is_target: bool,
502 ) {
503 let localdb = self.alpm.localdb();
504
505 if !is_target && localdb.pkgs().find_satisfier(pkg.pkg).is_some() {
506 return;
507 }
508
509 if self.flags.contains(Flags::NEEDED) {
510 if let Ok(local) = localdb.pkg(alpm_pkg.name()) {
511 if local.version() >= alpm_pkg.version() {
512 let unneeded = Unneeded::new(pkg.to_string(), local.version().to_string());
513 self.actions.unneeded.push(unneeded);
514 return;
515 }
516 }
517 }
518
519 let is_make = make.contains(&pkg.pkg);
520
521 self.stack.push(DepMissing::new(
522 alpm_pkg.name().to_string(),
523 pkg.pkg.to_string(),
524 ));
525 self.resolve_repo_pkg(alpm_pkg, is_target, is_make);
526 self.stack.pop().unwrap();
527 }
528
529 fn resolve_pkgbuild_target(
530 &mut self,
531 pkgbuild: Targ,
532 make: &HashSet<&str>,
533 is_target: bool,
534 targs: &[&str],
535 ) -> Result<(), Error> {
536 let dep = Depend::new(pkgbuild.pkg);
537 let localdb = self.alpm.localdb();
538
539 if self.should_skip_aur_pkg(&dep, is_target) {
540 return Ok(());
541 }
542
543 let (repo, base, pkg) =
544 if let Some((repo, base, pkg)) = self.find_pkgbuild_repo_dep(pkgbuild.repo, &dep) {
545 (repo, base, pkg)
546 } else {
547 self.actions.missing.push(Missing {
548 dep: dep.to_string(),
549 stack: self.stack.clone(),
550 });
551 return Ok(());
552 };
553
554 if self.flags.contains(Flags::NEEDED) || !is_target {
555 let is_devel = self
556 .is_devel
557 .get()
558 .map(|f| f(pkgbuild.pkg))
559 .unwrap_or(false);
560
561 if !is_devel {
562 if let Ok(local) = localdb.pkg(&*pkg.pkgname) {
563 if local.version() >= Version::new(base.version()) {
564 let unneeded =
565 Unneeded::new(pkgbuild.to_string(), local.version().to_string());
566 self.actions.unneeded.push(unneeded);
567 return Ok(());
568 }
569 }
570 }
571 }
572
573 let base = base.clone();
574 let pkg = pkg.clone();
575 let repo = repo.to_string();
576 let is_make = make.contains(&pkgbuild.pkg);
577 self.stack.push(DepMissing::new(
578 pkg.pkgname.to_string(),
579 pkgbuild.pkg.to_string(),
580 ));
581 self.resolve_aur_pkg_deps(targs, AurOrPkgbuild::Pkgbuild(&repo, &base, &pkg), is_make)?;
582 self.stack.pop().unwrap();
583
584 if self
585 .actions
586 .iter_aur_pkgs()
587 .any(|p| p.pkg.name == pkg.pkgname)
588 {
589 return Ok(());
590 }
591 if self
592 .actions
593 .iter_pkgbuilds()
594 .any(|p| p.1.pkg.pkgname == pkg.pkgname)
595 {
596 return Ok(());
597 }
598
599 let p = Pkgbuild {
600 pkg: pkg.clone(),
601 make: false,
602 target: is_target,
603 };
604
605 self.push_pkgbuild_build(repo.to_string(), base, p);
606 Ok(())
607 }
608
609 fn find_satisfier_aur_cache(&self, dep: &Dep) -> Option<&ArcPackage> {
610 if let Some(pkg) = self
611 .cache
612 .iter()
613 .filter(|pkg| self.alpm.localdb().pkg(pkg.name.as_str()).is_ok())
614 .find(|pkg| pkg.satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION)))
615 {
616 return Some(pkg);
617 }
618
619 if let Some(pkg) = self.cache.get(dep.name()) {
620 if pkg.satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION)) {
621 return Some(pkg);
622 }
623 }
624
625 self.cache
626 .iter()
627 .find(|pkg| pkg.satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION)))
628 }
629
630 fn select_satisfier_aur_cache(&self, dep: &Dep, target: bool) -> Option<&ArcPackage> {
634 debug!("select satisfier: {}", dep);
635 if let Some(f) = self.provider_callback.get() {
636 let mut pkgs = self
637 .cache
638 .iter()
639 .filter(|pkg| pkg.satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION)))
640 .map(|pkg| pkg.name.as_str())
641 .collect::<Vec<_>>();
642
643 debug!("satisfiers for '{:?}': {:?})", dep.to_string(), pkgs);
644
645 if !target {
646 if let Some(pkg) = pkgs.iter().find(|&&p| self.alpm.localdb().pkg(p).is_ok()) {
647 debug!("picked from cache: {}", pkg);
648 return self.cache.get(*pkg);
649 }
650 if let Some(pkg) = pkgs.iter().find(|&&p| p == dep.name()) {
651 debug!("picked from cache: {}", pkg);
652 return self.cache.get(*pkg);
653 }
654 }
655
656 if pkgs.len() == 1 {
657 return self.cache.get(pkgs[0]);
658 } else if pkgs.is_empty() {
659 return None;
660 }
661
662 if !target {
663 for &pkg in &pkgs {
664 if self.alpm.localdb().pkg(pkg).is_ok() {
665 return self.cache.get(pkg);
666 }
667 }
668 }
669
670 if let Some(true_pkg) = pkgs.iter().position(|pkg| *pkg == dep.name()) {
671 pkgs.swap(true_pkg, 0);
672 pkgs[1..].sort_unstable();
673 } else {
674 pkgs.sort_unstable();
675 }
676
677 let choice = f(dep.to_string().as_str(), &pkgs);
678 debug!("choice was: {}={}", choice, pkgs[choice]);
679 self.cache.get(pkgs[choice])
680 } else {
681 debug!("no provider callback");
682 self.find_satisfier_aur_cache(dep)
683 }
684 }
685
686 fn resolve_aur_pkg_deps(
687 &mut self,
688 targs: &[&str],
689 pkg: AurOrPkgbuild,
690 make: bool,
691 ) -> Result<(), Error> {
692 debug!("resolve pkgbuild repo pkg deps: {}", pkg.pkgbase());
693 let nover = self.flags.contains(Flags::NO_DEP_VERSION);
694 let arch = self.alpm.architectures().first().unwrap_or("");
695 let check = self.flags.contains(Flags::CHECK_DEPENDS);
696 if !self.flags.contains(Flags::NO_DEPS) {
697 for dep_str in pkg.depends(arch, check) {
698 debug!("depend: {}", dep_str);
699 let dep = Depend::new(dep_str.to_string());
700
701 if self.assume_installed(&dep)
702 || self.satisfied_build(&dep)
703 || self.resolved.contains(&dep.to_string())
704 || self.satisfied_install(&dep)
705 {
706 continue;
707 }
708
709 let same_pkgbuild = matches!(pkg, AurOrPkgbuild::Pkgbuild(_, base, _) if base
710 .which_satisfies_dep(&dep, nover)
711 .is_some()
712 && !self.pkgbuild_dep_in_base(&dep.to_string(), &base, arch, check));
713
714 let is_aur_targ = !same_pkgbuild && self.dep_is_aur_targ(targs, &dep);
715 self.resolved.insert(dep.to_string());
716
717 if !is_aur_targ && !same_pkgbuild {
718 if !self.flags.contains(Flags::RESOLVE_SATISFIED_PKGBUILDS)
719 && self.satisfied_local(&dep)
720 {
721 continue;
722 }
723 let depstr = dep.to_string();
724 if let Some(pkg) = self.find_repo_satisfier(&depstr) {
725 if !self.satisfied_local(&dep) {
726 self.stack
727 .push(DepMissing::new(pkg.name().to_string(), depstr));
728 self.resolve_repo_pkg(pkg, false, true);
729 self.stack.pop().unwrap();
730 }
731 continue;
732 }
733 }
734
735 if !same_pkgbuild && self.should_skip_aur_pkg(&dep, is_aur_targ) {
736 continue;
737 }
738
739 if let Some((repo, base, pkg)) = self.find_pkgbuild_repo_dep(None, &dep) {
740 let repo = repo.to_string();
741 let base = base.clone();
742 let pkg = pkg.clone();
743 self.stack
744 .push(DepMissing::new(pkg.pkgname.to_string(), dep.to_string()));
745 self.resolve_aur_pkg_deps(
746 targs,
747 AurOrPkgbuild::Pkgbuild(&repo, &base, &pkg),
748 true,
749 )?;
750 self.stack.pop();
751
752 let pkg = Pkgbuild {
753 pkg,
754 make,
755 target: false,
756 };
757
758 self.push_pkgbuild_build(repo.to_string(), base, pkg);
759 continue;
760 }
761
762 let sat_pkg = if let Some(pkg) = self.select_satisfier_aur_cache(&dep, false) {
763 pkg.clone()
764 } else {
765 debug!(
766 "failed to find '{}' in pkgbuild repo or aur cache",
767 dep.to_string(),
768 );
769 if log_enabled!(Debug) {
770 debug!(
771 "at time of failure pkgcache is: {:?}\n",
772 self.cache.iter().map(|p| &p.name).collect::<Vec<_>>()
773 );
774 debug!("stack is: {:?}", self.stack);
775 }
776
777 self.actions.missing.push(Missing {
778 dep: dep.to_string(),
779 stack: self.stack.clone(),
780 });
781 continue;
782 };
783
784 self.stack
785 .push(DepMissing::new(sat_pkg.name.to_string(), dep.to_string()));
786 self.resolve_aur_pkg_deps(targs, AurOrPkgbuild::Aur(&sat_pkg), true)?;
787 self.stack.pop();
788
789 let p = AurPackage {
790 pkg: sat_pkg.clone(),
791 make,
792 target: false,
793 };
794
795 self.push_aur_build(&sat_pkg.package_base, p);
796 }
797 }
798
799 Ok(())
800 }
801
802 fn find_pkgbuild_repo_dep(
803 &self,
804 repo_targ: Option<&str>,
805 dep: &Depend,
806 ) -> Option<(&str, &srcinfo::Srcinfo, &srcinfo::Package)> {
807 for repo in &self.repos {
808 if repo_targ.is_some() && Some(repo.name) != repo_targ {
809 continue;
810 }
811
812 for base in &repo.pkgs {
813 if let Some(pkg) =
814 base.which_satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION))
815 {
816 return Some((&repo.name, base, base.pkg(pkg).unwrap()));
817 }
818 }
819 }
820 None
821 }
822
823 fn should_skip_aur_pkg(&self, dep: &Depend, is_target: bool) -> bool {
824 if is_target {
825 return false;
826 }
827 if self.flags.contains(Flags::RESOLVE_SATISFIED_PKGBUILDS) {
828 return false;
829 }
830 if self.assume_installed(dep) {
831 return true;
832 }
833 if self.satisfied_local(dep) {
834 return true;
835 }
836
837 false
838 }
839
840 fn resolve_repo_pkg(&mut self, pkg: &'a alpm::Package, target: bool, make: bool) {
841 if !self.seen.insert(pkg.name().to_string()) {
842 return;
843 }
844
845 if !self.flags.contains(Flags::NO_DEPS) {
846 for dep in pkg.depends() {
847 if self.satisfied_install(&dep)
848 || self.satisfied_local(&dep)
849 || self.assume_installed(&dep)
850 {
851 continue;
852 }
853
854 if let Some(pkg) = self.find_repo_satisfier(dep.to_string()) {
855 self.stack
856 .push(DepMissing::new(pkg.name().to_string(), dep.to_string()));
857 self.resolve_repo_pkg(pkg, false, true);
858 self.stack.pop().unwrap();
859 } else {
860 self.actions.missing.push(Missing {
861 dep: dep.to_string(),
862 stack: self.stack.clone(),
863 });
864 }
865 }
866 }
867
868 debug!("pushing to install: {}", pkg.name());
869 self.actions.install.push(RepoPackage { pkg, make, target });
870 }
871
872 async fn cache_aur_pkgs<S: AsRef<str>>(
873 &mut self,
874 pkgs: &[S],
875 target: bool,
876 ) -> Result<Vec<ArcPackage>, Error> {
877 let mut pkgs_nover = pkgs
878 .iter()
879 .map(|p| p.as_ref().split(is_ver_char).next().unwrap())
880 .collect::<Vec<_>>();
881 pkgs_nover.sort_unstable();
882 pkgs_nover.dedup();
883
884 if (self.flags.contains(Flags::PROVIDES))
885 || (target && self.flags.contains(Flags::TARGET_PROVIDES))
886 {
887 self.cache_provides(&pkgs_nover).await
888 } else {
889 let mut info = self
890 .raur
891 .cache_info(self.cache, &pkgs_nover)
892 .await
893 .map_err(|e| Error::Raur(Box::new(e)))?;
894
895 if (self.flags.contains(Flags::PROVIDES))
896 || (self.flags.contains(Flags::MISSING_PROVIDES))
897 {
898 let missing = pkgs
899 .iter()
900 .map(|pkg| Depend::new(pkg.as_ref()))
901 .filter(|dep| {
902 !info.iter().any(|info| {
903 info.satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION))
904 })
905 })
906 .map(|dep| dep.to_string())
907 .collect::<Vec<_>>();
908
909 if !missing.is_empty() {
910 debug!("attempting to find provides for missing: {:?}", missing);
911 info.extend(self.cache_provides(&missing).await?);
912 }
913 }
914
915 if log_enabled!(Debug) {
916 debug!(
917 "provides resolved {:?} found {:?}",
918 pkgs.iter().map(AsRef::as_ref).collect::<Vec<_>>(),
919 info.iter().map(|p| p.name.clone()).collect::<Vec<_>>()
920 );
921 }
922 Ok(info)
923 }
924 }
925
926 async fn cache_provides<S: AsRef<str>>(
927 &mut self,
928 pkgs: &[S],
929 ) -> Result<Vec<ArcPackage>, Error> {
930 let mut to_info = pkgs
931 .iter()
932 .map(|s| s.as_ref().to_string())
933 .collect::<Vec<_>>();
934
935 debug!("cache args: {:?}\n", to_info);
936 for pkg in pkgs {
937 let pkg = pkg.as_ref().split(is_ver_char).next().unwrap();
938
939 if self.alpm.localdb().pkg(pkg).is_ok() {
946 continue;
947 }
948
949 to_info.extend(
950 self.raur
951 .search_by(pkg, SearchBy::Provides)
952 .await
953 .unwrap_or_else(|e| {
954 debug!("provide search '{}' failed: {}", pkg, e);
955 Vec::new()
956 })
957 .into_iter()
958 .map(|p| p.name),
959 );
960 }
961
962 to_info.sort();
963 to_info.dedup();
964
965 debug!("trying to cache {:?}\n", to_info);
966
967 let mut ret = self
968 .raur
969 .cache_info(self.cache, &to_info)
970 .await
971 .map_err(|e| Error::Raur(Box::new(e)))?;
972
973 ret.retain(|pkg| {
974 pkgs.iter().any(|dep| {
975 pkg.satisfies_dep(
976 &Depend::new(dep.as_ref()),
977 self.flags.contains(Flags::NO_DEP_VERSION),
978 )
979 })
980 });
981
982 Ok(ret)
983 }
984
985 async fn cache_aur_pkgs_recursive<S: AsRef<str>>(
986 &mut self,
987 pkgs: &[S],
988 pkgbuilds: &[Targ<'_>],
989 target: bool,
990 ) -> Result<(), Error> {
991 let mut new_pkgs = self
992 .cache_aur_pkgs_recursive2(pkgs, pkgbuilds, target)
993 .await?;
994
995 while !new_pkgs.is_empty() {
996 let (pkgbuild_pkgs, pkgs): (Vec<_>, Vec<_>) = new_pkgs.into_iter().partition(|p| {
997 self.find_pkgbuild_repo_dep(None, &Depend::new(p.as_str()))
998 .is_some()
999 });
1000 let pkgbuild_pkgs = pkgbuild_pkgs
1001 .iter()
1002 .map(|p| Targ::new(None, p))
1003 .collect::<Vec<_>>();
1004
1005 new_pkgs = self
1006 .cache_aur_pkgs_recursive2(&pkgs, &pkgbuild_pkgs, false)
1007 .await?;
1008 }
1009
1010 Ok(())
1011 }
1012
1013 fn find_aur_deps_of_pkgbuild(&mut self, targ: Targ<'_>) -> Vec<String> {
1014 let mut ret = Vec::new();
1015 if self.flags.contains(Flags::NO_DEPS) {
1016 return Vec::new();
1017 }
1018 let mut new_resolved = HashSet::new();
1019 let mut pkgbuilds = Vec::new();
1020
1021 let pkg = Depend::new(targ.pkg);
1022 if let Some((repo, base, pkg)) = self.find_pkgbuild_repo_dep(targ.repo, &pkg) {
1023 let arch = self.alpm.architectures().first().unwrap_or("");
1024 let pkgbuild = AurOrPkgbuild::Pkgbuild(repo, base, pkg);
1025 let deps = pkgbuild.depends(arch, self.flags.contains(Flags::CHECK_DEPENDS));
1026
1027 for pkg in deps {
1028 let dep = Depend::new(pkg);
1029
1030 if self.resolved.contains(&dep.to_string()) {
1031 continue;
1032 }
1033 if self.find_repo_satisfier_silent(pkg).is_some() {
1034 continue;
1035 }
1036 if !self.flags.contains(Flags::RESOLVE_SATISFIED_PKGBUILDS) {
1037 if self.satisfied_local(&dep) {
1038 continue;
1039 }
1040 } else if self.assume_installed(&dep) {
1041 continue;
1042 }
1043
1044 new_resolved.insert(dep.to_string());
1045 if self.find_pkgbuild_repo_dep(None, &dep).is_some() {
1046 pkgbuilds.push(dep.to_depend());
1047 continue;
1048 }
1049
1050 ret.push(pkg.to_string());
1051 }
1052 }
1053
1054 self.resolved.extend(new_resolved);
1055 for dep in pkgbuilds {
1056 ret.extend(self.find_aur_deps_of_pkgbuild(Targ::new(None, &dep.to_string())));
1057 }
1058
1059 ret
1060 }
1061
1062 async fn cache_aur_pkgs_recursive2<S: AsRef<str>>(
1063 &mut self,
1064 pkgs: &[S],
1065 pkgbuild_pkgs: &[Targ<'_>],
1066 target: bool,
1067 ) -> Result<Vec<String>, Error> {
1068 let pkgs = pkgbuild_pkgs
1069 .iter()
1070 .flat_map(|p| self.find_aur_deps_of_pkgbuild(*p))
1071 .chain(pkgs.iter().map(|p| p.as_ref().to_string()))
1072 .collect::<Vec<_>>();
1073
1074 if pkgs.is_empty() {
1075 return Ok(Vec::new());
1076 }
1077
1078 if log_enabled!(Debug) {
1079 debug!(
1080 "cache_aur_pkgs_recursive {:?}",
1081 pkgs.iter().collect::<Vec<_>>()
1082 )
1083 }
1084
1085 let pkgs = self.cache_aur_pkgs(&pkgs, target).await?;
1086 if self.flags.contains(Flags::NO_DEPS) {
1087 return Ok(Vec::new());
1088 }
1089
1090 let mut new_pkgs = Vec::new();
1091 for pkg in pkgs {
1092 let check = if self.flags.contains(Flags::CHECK_DEPENDS) {
1093 Some(&pkg.check_depends)
1094 } else {
1095 None
1096 };
1097
1098 let depends = pkg
1099 .depends
1100 .iter()
1101 .chain(&pkg.make_depends)
1102 .chain(check.into_iter().flatten());
1103
1104 for pkg in depends {
1105 let dep = Depend::new(pkg.as_str());
1106
1107 if self.resolved.contains(&dep.to_string()) {
1108 continue;
1109 }
1110 if self.find_repo_satisfier_silent(pkg).is_some() {
1111 continue;
1112 }
1113 if !self.flags.contains(Flags::RESOLVE_SATISFIED_PKGBUILDS) {
1114 if self.satisfied_local(&dep) {
1115 continue;
1116 }
1117 } else if self.assume_installed(&dep) {
1118 continue;
1119 }
1120
1121 self.resolved.insert(dep.to_string());
1122 new_pkgs.push(pkg.clone());
1123 }
1124 }
1125
1126 Ok(new_pkgs)
1127 }
1128
1129 fn satisfied_build(&self, target: &Dep) -> bool {
1130 for build in &self.actions.build {
1131 match build {
1132 Base::Aur(pkgs) => {
1133 if pkgs.pkgs.iter().any(|b| {
1134 b.pkg
1135 .satisfies_dep(target, self.flags.contains(Flags::NO_DEP_VERSION))
1136 }) {
1137 return true;
1138 }
1139 }
1140 Base::Pkgbuild(pkgs) => {
1141 if pkgs.pkgs.iter().any(|pkg| {
1142 (&*pkgs.srcinfo, &pkg.pkg)
1143 .satisfies_dep(target, self.flags.contains(Flags::NO_DEP_VERSION))
1144 }) {
1145 return true;
1146 }
1147 }
1148 }
1149 }
1150 false
1151 }
1152
1153 fn satisfied_install(&self, target: &Dep) -> bool {
1154 self.actions.install.iter().any(|install| {
1155 install
1156 .pkg
1157 .satisfies_dep(target, self.flags.contains(Flags::NO_DEP_VERSION))
1158 })
1159 }
1160
1161 fn satisfied_local(&self, target: &Dep) -> bool {
1162 if let Ok(pkg) = self.alpm.localdb().pkg(target.name()) {
1163 if pkg.satisfies_dep(target, self.flags.contains(Flags::NO_DEP_VERSION)) {
1164 return true;
1165 }
1166 }
1167
1168 if self.flags.contains(Flags::NO_DEP_VERSION) {
1169 let ret = self.alpm.localdb().pkgs().find_satisfier(target.name());
1170 ret.is_some()
1171 } else {
1172 let ret = self
1173 .alpm
1174 .localdb()
1175 .pkgs()
1176 .find_satisfier(target.to_string());
1177 ret.is_some()
1178 }
1179 }
1180
1181 fn find_repo_target_satisfier(&self, mut target: Targ) -> Option<&'a alpm::Package> {
1182 if self.flags.contains(Flags::NO_DEP_VERSION) {
1183 target = Targ {
1184 repo: target.repo,
1185 pkg: target
1186 .pkg
1187 .split_once(is_ver_char)
1188 .map_or(target.pkg, |x| x.0),
1189 };
1190 }
1191
1192 self.alpm.syncdbs().find_target_satisfier(target)
1193 }
1194
1195 fn find_repo_satisfier<S: AsRef<str>>(&self, target: S) -> Option<&'a alpm::Package> {
1196 let mut target = target.as_ref();
1197
1198 if self.flags.contains(Flags::NO_DEP_VERSION) {
1199 target = target.split_once(is_ver_char).map_or(target, |x| x.0)
1200 }
1201
1202 self.alpm.syncdbs().find_satisfier(target)
1203 }
1204
1205 fn find_repo_satisfier_silent<S: AsRef<str>>(&self, target: S) -> Option<&'a alpm::Package> {
1206 let cb = self.alpm.take_raw_question_cb();
1207 let pkg = self.find_repo_satisfier(target);
1208 self.alpm.set_raw_question_cb(cb);
1209 pkg
1210 }
1211
1212 fn dep_is_aur_targ(&self, targs: &[&str], dep: &Dep) -> bool {
1213 if let Some(pkg) = self.find_satisfier_aur_cache(dep) {
1214 for &targ in targs {
1215 if self.seen_target.contains(targ) {
1216 continue;
1217 }
1218 if pkg.satisfies_dep(
1219 &Depend::new(targ),
1220 self.flags.contains(Flags::NO_DEP_VERSION),
1221 ) {
1222 return true;
1223 }
1224 }
1225 }
1226
1227 false
1228 }
1229
1230 fn pkgbuild_dep_in_base(
1231 &self,
1232 dep: &str,
1233 base: &srcinfo::Srcinfo,
1234 arch: &str,
1235 check_depends: bool,
1236 ) -> bool {
1237 let check = if check_depends {
1238 base.base.checkdepends.as_slice()
1239 } else {
1240 &[]
1241 };
1242
1243 base.base
1246 .makedepends
1247 .iter()
1248 .chain(check)
1249 .filter(|v| v.supports(arch))
1250 .flat_map(|d| &d.vec)
1251 .any(|d| d == dep)
1252 }
1253
1254 fn push_aur_build(&mut self, pkgbase: &str, pkg: AurPackage) {
1255 debug!("pushing to build: {}", pkg.pkg.name);
1256 let mut build = true;
1257
1258 if let Some(Base::Aur(base)) = self.actions.build.last_mut() {
1259 if base.package_base() == pkgbase {
1260 base.pkgs.push(pkg);
1261 return;
1262 }
1263 }
1264
1265 for base in self.actions.build.iter_mut() {
1266 if let Base::Aur(pkgs) = base {
1267 if pkgs.pkgs[0].pkg.package_base == pkgbase {
1268 build = false;
1269 break;
1270 }
1271 }
1272 }
1273
1274 self.actions.build.push(Base::Aur(AurBase {
1275 pkgs: vec![pkg],
1276 build,
1277 }));
1278 }
1279
1280 fn push_pkgbuild_build(&mut self, repo: String, base: srcinfo::Srcinfo, pkg: Pkgbuild) {
1282 debug!("pushing to build: {}", pkg.pkg.pkgname);
1283 let mut b = true;
1284
1285 if let Some(Base::Pkgbuild(b)) = self.actions.build.last_mut() {
1286 if b.package_base() == base.base.pkgbase {
1287 b.pkgs.push(pkg);
1288 return;
1289 }
1290 }
1291
1292 for build in self.actions.build.iter_mut() {
1293 if let Base::Pkgbuild(pkgs) = build {
1294 if pkgs.srcinfo.base.pkgbase == base.base.pkgbase {
1295 b = false;
1296 break;
1297 }
1298 }
1299 }
1300
1301 self.actions.build.push(Base::Pkgbuild(PkgbuildPackages {
1302 repo,
1303 srcinfo: Box::new(base),
1304 pkgs: vec![pkg],
1305 build: b,
1306 }));
1307 }
1308
1309 pub(crate) fn find_pkgbuild(
1310 &self,
1311 name: &str,
1312 ) -> Option<(&'a str, &'a srcinfo::Srcinfo, &'a srcinfo::Package)> {
1313 for repo in &self.repos {
1314 for &srcinfo in &repo.pkgs {
1315 for pkg in &srcinfo.pkgs {
1316 if pkg.pkgname == name {
1317 return Some((repo.name, srcinfo, pkg));
1318 }
1319 }
1320 }
1321 }
1322 None
1323 }
1324
1325 pub(crate) fn is_pkgbuild(&self, name: &str) -> bool {
1326 self.find_pkgbuild(name).is_some()
1327 }
1328
1329 fn calculate_make(&mut self) {
1330 let mut runtime = Vec::new();
1331 let mut run = true;
1332 let no_dep_ver = self.flags.contains(Flags::NO_DEP_VERSION);
1333 let arch = self.alpm.architectures().first();
1334
1335 self.actions
1336 .install
1337 .iter()
1338 .filter(|p| !p.make)
1339 .for_each(|p| runtime.extend(p.pkg.depends().iter().map(|d| d.to_depend())));
1340 self.actions
1341 .iter_aur_pkgs()
1342 .filter(|p| !p.make)
1343 .for_each(|p| runtime.extend(p.pkg.depends.iter().map(|d| Depend::new(d.as_str()))));
1344 self.actions
1345 .iter_pkgbuilds()
1346 .filter(|p| !p.1.make)
1347 .for_each(|p| {
1348 runtime.extend(
1349 p.1.pkg
1350 .depends
1351 .iter()
1352 .filter(|d| {
1353 d.arch.is_none()
1354 || d.arch.as_deref() == self.alpm.architectures().first()
1355 })
1356 .flat_map(|d| &d.vec)
1357 .map(|d| Depend::new(d.as_str())),
1358 )
1359 });
1360
1361 runtime.sort_unstable_by_key(|a| a.to_string());
1362 runtime.dedup_by_key(|a| a.to_string());
1363
1364 while run {
1365 run = false;
1366 for pkg in &mut self.actions.install {
1367 if !pkg.make {
1368 continue;
1369 }
1370
1371 let satisfied = runtime
1372 .iter()
1373 .any(|dep| pkg.pkg.satisfies_dep(dep, no_dep_ver));
1374
1375 if satisfied {
1376 pkg.make = false;
1377 run = true;
1378 runtime.extend(pkg.pkg.depends().iter().map(|d| d.to_depend()));
1379 }
1380 }
1381
1382 for base in &mut self.actions.build {
1383 match base {
1384 Base::Aur(base) => {
1385 for pkg in &mut base.pkgs {
1386 if !pkg.make {
1387 continue;
1388 }
1389
1390 let satisfied = runtime
1391 .iter()
1392 .any(|dep| pkg.pkg.satisfies_dep(dep, no_dep_ver));
1393
1394 if satisfied {
1395 pkg.make = false;
1396 run = true;
1397 runtime.extend(
1398 pkg.pkg.depends.iter().map(|d| Depend::new(d.as_str())),
1399 );
1400 }
1401 }
1402 }
1403 Base::Pkgbuild(pkgs) => {
1404 for pkg in &mut pkgs.pkgs {
1405 if !pkg.make {
1406 continue;
1407 }
1408
1409 let satisfied = runtime.iter().any(|dep| {
1410 (&*pkgs.srcinfo, &pkg.pkg).satisfies_dep(dep, no_dep_ver)
1411 });
1412
1413 if satisfied {
1414 pkg.make = false;
1415 run = true;
1416 runtime.extend(
1417 pkg.pkg
1418 .depends
1419 .iter()
1420 .filter(|d| d.arch.is_none() || d.arch.as_deref() == arch)
1421 .flat_map(|d| &d.vec)
1422 .map(|d| Depend::new(d.as_str())),
1423 );
1424 }
1425 }
1426 }
1427 }
1428 }
1429 }
1430 }
1431
1432 fn assume_installed(&self, dep: &Dep) -> bool {
1433 let nover = self.flags.contains(Flags::NO_DEP_VERSION);
1434 self.alpm
1435 .assume_installed()
1436 .iter()
1437 .any(|assume| satisfies_provide(dep, &assume, nover))
1438 }
1439}
1440
1441fn is_ver_char(c: char) -> bool {
1442 matches!(c, '<' | '=' | '>')
1443}
1444
1445#[cfg(test)]
1446mod tests {
1447 use super::*;
1448 use crate::tests::*;
1449 use crate::Conflict;
1450 use alpm::SigLevel;
1451 use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode};
1452
1453 struct TestActions {
1454 build: Vec<String>,
1455 install: Vec<String>,
1456 missing: Vec<Vec<String>>,
1457 make: usize,
1458 duplicates: Vec<String>,
1459 targets: Vec<String>,
1460 }
1461
1462 fn _init_logger() {
1463 let _ = TermLogger::init(
1464 LevelFilter::Trace,
1465 ConfigBuilder::new()
1466 .add_filter_allow_str("aur_depends")
1467 .build(),
1468 TerminalMode::Stderr,
1469 ColorChoice::Never,
1470 );
1471 }
1472
1473 fn alpm() -> Alpm {
1474 let mut handle = Alpm::new("/", "tests/db").unwrap();
1476 handle.register_syncdb("core", SigLevel::NONE).unwrap();
1477 handle.register_syncdb("extra", SigLevel::NONE).unwrap();
1478 handle.register_syncdb("community", SigLevel::NONE).unwrap();
1479 handle.register_syncdb("multilib", SigLevel::NONE).unwrap();
1480 handle
1481 .add_assume_installed(&Depend::new("assume-dep1"))
1482 .unwrap();
1483 handle.add_assume_installed(&Depend::new("i3-wm")).unwrap();
1484 handle
1485 }
1486
1487 async fn resolve(pkgs: &[&str], flags: Flags) -> TestActions {
1488 let raur = raur();
1490 let alpm = alpm();
1491 let mut cache = HashSet::new();
1492 let srcinfo = srcinfo::Srcinfo::parse_file("tests/srcinfo/custom.SRCINFO").unwrap();
1493 let srcinfo = vec![&srcinfo];
1494
1495 let repo = vec![PkgbuildRepo {
1496 name: "my_repo",
1497 pkgs: srcinfo,
1498 }];
1499
1500 let handle = Resolver::new(&alpm, &mut cache, &raur, flags)
1501 .pkgbuild_repos(repo)
1502 .aur_namespace(true)
1503 .provider_callback(|_, pkgs| {
1504 debug!("provider choice: {:?}", pkgs);
1505 if let Some(i) = pkgs.iter().position(|pkg| *pkg == "yay-bin") {
1506 i
1507 } else {
1508 0
1509 }
1510 });
1511
1512 let actions = handle.resolve_targets(pkgs).await.unwrap();
1513
1514 let mut build = actions
1515 .iter_aur_pkgs()
1516 .map(|p| p.pkg.name.clone())
1517 .collect::<Vec<_>>();
1518 build.extend(actions.iter_pkgbuilds().map(|p| p.1.pkg.pkgname.clone()));
1519
1520 let mut install = actions
1521 .install
1522 .iter()
1523 .map(|b| b.pkg.name().to_string())
1524 .collect::<Vec<_>>();
1525
1526 build.sort();
1527 install.sort();
1528
1529 let make = actions.install.iter().filter(|i| i.make).count()
1530 + actions.iter_aur_pkgs().filter(|i| i.make).count()
1531 + actions.iter_pkgbuilds().filter(|i| i.1.make).count();
1532
1533 let mut targets = actions
1534 .iter_aur_pkgs()
1535 .filter(|pkg| pkg.target)
1536 .map(|pkg| pkg.pkg.name.to_string())
1537 .collect::<Vec<_>>();
1538
1539 targets.extend(
1540 actions
1541 .iter_pkgbuilds()
1542 .filter(|pkg| pkg.1.target)
1543 .map(|pkg| pkg.1.pkg.pkgname.to_string()),
1544 );
1545
1546 TestActions {
1547 duplicates: actions.duplicate_targets(),
1548 install,
1549 build,
1550 missing: actions
1551 .missing
1552 .into_iter()
1553 .map(|m| {
1554 m.stack
1555 .into_iter()
1556 .map(|s| s.pkg)
1557 .chain(Some(m.dep))
1558 .collect()
1559 })
1560 .collect(),
1561 make,
1562 targets,
1563 }
1564 }
1565
1566 #[tokio::test]
1567 async fn test_yay() {
1568 let TestActions {
1569 install,
1570 build,
1571 make,
1572 ..
1573 } = resolve(&["yay"], Flags::new()).await;
1574
1575 assert_eq!(build, vec!["yay-bin"]);
1576 assert_eq!(
1577 install,
1578 vec!["git", "go", "perl-error", "perl-mailtools", "perl-timedate"]
1579 );
1580 assert_eq!(make, 1);
1581 }
1582
1583 #[tokio::test]
1584 async fn test_yay_needed() {
1585 let TestActions { install, build, .. } =
1586 resolve(&["yay"], Flags::new() | Flags::NEEDED).await;
1587
1588 assert_eq!(build, vec!["yay-bin"]);
1589 assert_eq!(
1590 install,
1591 vec!["git", "go", "perl-error", "perl-mailtools", "perl-timedate"]
1592 );
1593 }
1594
1595 #[tokio::test]
1596 async fn test_yay_no_deps() {
1597 let TestActions { install, build, .. } =
1598 resolve(&["yay"], Flags::new() | Flags::NO_DEPS).await;
1599
1600 assert_eq!(build, vec!["yay-bin"]);
1601 assert_eq!(install, Vec::<String>::new());
1602 }
1603
1604 #[tokio::test]
1605 async fn test_aur_yay_no_deps() {
1606 let TestActions { install, build, .. } =
1607 resolve(&["aur/yay"], Flags::new() | Flags::NO_DEPS).await;
1608
1609 assert_eq!(build, vec!["yay-bin"]);
1610 assert_eq!(install, Vec::<String>::new());
1611 }
1612
1613 #[tokio::test]
1614 async fn test_core_yay_no_deps() {
1615 let TestActions { install, build, .. } =
1616 resolve(&["core/yay"], Flags::new() | Flags::NO_DEPS).await;
1617
1618 assert_eq!(build, Vec::<String>::new());
1619 assert_eq!(install, Vec::<String>::new());
1620 }
1621
1622 #[tokio::test]
1623 async fn test_core_glibc_no_deps() {
1624 let TestActions { install, build, .. } =
1625 resolve(&["core/glibc"], Flags::new() | Flags::NO_DEPS).await;
1626
1627 assert_eq!(build, Vec::<String>::new());
1628 assert_eq!(install, vec!["glibc"]);
1629 }
1630
1631 #[tokio::test]
1632 async fn test_aur_glibc_no_deps() {
1633 let TestActions { install, build, .. } =
1634 resolve(&["core/yay"], Flags::new() | Flags::NO_DEPS).await;
1635
1636 assert_eq!(build, Vec::<String>::new());
1637 assert_eq!(install, Vec::<String>::new());
1638 }
1639
1640 #[tokio::test]
1641 async fn test_extra_glibc_no_deps() {
1642 let TestActions { install, build, .. } =
1643 resolve(&["core/yay"], Flags::new() | Flags::NO_DEPS).await;
1644
1645 assert_eq!(build, Vec::<String>::new());
1646 assert_eq!(install, Vec::<String>::new());
1647 }
1648
1649 #[tokio::test]
1650 async fn test_yay_no_provides() {
1651 let TestActions { install, build, .. } =
1652 resolve(&["yay"], Flags::new() & !Flags::TARGET_PROVIDES).await;
1653
1654 assert_eq!(build, vec!["yay"]);
1655 assert_eq!(
1656 install,
1657 vec!["git", "go", "perl-error", "perl-mailtools", "perl-timedate"]
1658 );
1659 }
1660
1661 #[tokio::test]
1662 async fn test_make_only() {
1663 let TestActions { make, .. } = resolve(
1664 &["ros-melodic-desktop-full"],
1665 Flags::new() & !Flags::TARGET_PROVIDES & !Flags::MISSING_PROVIDES,
1666 )
1667 .await;
1668 assert_eq!(make, 40);
1669 }
1670
1671 #[tokio::test]
1672 async fn test_cache_only() {
1673 let raur = raur();
1674 let alpm = alpm();
1675 let mut cache = HashSet::new();
1676
1677 let mut handle = Resolver::new(&alpm, &mut cache, &raur, Flags::new());
1678 handle
1679 .cache_aur_pkgs_recursive(&["ros-melodic-desktop-full"], &[], true)
1680 .await
1681 .unwrap();
1682 }
1683
1684 #[tokio::test]
1685 async fn test_pacaur() {
1686 let TestActions { install, build, .. } = resolve(&["pacaur"], Flags::new()).await;
1687 assert_eq!(build, vec!["auracle-git", "pacaur"]);
1688 assert_eq!(
1689 install,
1690 vec![
1691 "git",
1692 "jq",
1693 "libnsl",
1694 "meson",
1695 "ninja",
1696 "oniguruma",
1697 "perl-error",
1698 "perl-mailtools",
1699 "perl-timedate",
1700 "python",
1701 "python-appdirs",
1702 "python-packaging",
1703 "python-pyparsing",
1704 "python-setuptools",
1705 "python-six"
1706 ]
1707 );
1708 }
1709
1710 #[tokio::test]
1711 async fn test_wants_pacaur() {
1712 let TestActions { build, .. } = resolve(&["wants-pacaur"], Flags::new()).await;
1713 assert_eq!(build, vec!["wants-pacaur"]);
1714 }
1715
1716 #[tokio::test]
1717 async fn test_wants_pacaur_force_deps() {
1718 let TestActions { build, .. } = resolve(
1719 &["wants-pacaur"],
1720 Flags::new() | Flags::RESOLVE_SATISFIED_PKGBUILDS,
1721 )
1722 .await;
1723 assert_eq!(build, vec!["auracle-git", "pacaur", "wants-pacaur"]);
1724 }
1725
1726 #[tokio::test]
1727 async fn test_pkgbuild() {
1728 let TestActions { install, build, .. } = resolve(&["custom"], Flags::new()).await;
1729 assert_eq!(build, vec!["c1", "c2", "c3", "custom"]);
1730 assert_eq!(install, vec!["libedit", "llvm-libs", "rust"]);
1731 }
1732
1733 #[tokio::test]
1734 async fn test_assume() {
1735 let TestActions { install, build, .. } = resolve(&["assume-test"], Flags::new()).await;
1736 assert_eq!(build, vec!["assume-dep2", "assume-test"]);
1737 assert_eq!(install, vec!["libev"]);
1738 }
1739
1740 #[tokio::test]
1741 async fn test_pacaur_needed() {
1742 let TestActions { install, build, .. } =
1743 resolve(&["pacaur"], Flags::new() | Flags::NEEDED).await;
1744 assert_eq!(build, Vec::<String>::new());
1745 assert_eq!(install, Vec::<String>::new());
1746 }
1747
1748 #[tokio::test]
1749 async fn test_many() {
1750 let TestActions { install, build, .. } = resolve(
1751 &["yay", "pacaur", "pacman", "glibc", "0ad", "spotify"],
1752 Flags::new() | Flags::NO_DEPS,
1753 )
1754 .await;
1755
1756 assert_eq!(build, vec!["pacaur", "spotify", "yay-bin"]);
1757 assert_eq!(install, vec!["0ad", "glibc", "pacman"]);
1758 }
1759
1760 #[tokio::test]
1761 async fn test_many_needed() {
1762 let TestActions { install, build, .. } = resolve(
1763 &["yay", "pacaur", "pacman", "glibc", "0ad", "spotify"],
1764 Flags::new() | Flags::NO_DEPS | Flags::NEEDED,
1765 )
1766 .await;
1767
1768 assert_eq!(build, vec!["spotify", "yay-bin"]);
1769 assert_eq!(install, vec!["0ad", "glibc"]);
1770 }
1771
1772 #[tokio::test]
1773 async fn test_override_dep_via_target() {
1774 let TestActions { install, build, .. } = resolve(&["perl-mailtools"], Flags::new()).await;
1775 assert_eq!(install, ["perl-mailtools", "perl-timedate"]);
1776 assert!(build.is_empty());
1777
1778 let TestActions { install, build, .. } =
1779 resolve(&["perl-mailtools-git"], Flags::new()).await;
1780 assert_eq!(install, ["perl-timedate"]);
1781 assert_eq!(build, ["perl-mailtools-git"]);
1782
1783 let TestActions { install, build, .. } =
1784 resolve(&["perl-timedate-git"], Flags::new()).await;
1785 assert!(install.is_empty());
1786 assert_eq!(build, ["perl-timedate-git"]);
1787
1788 let TestActions { install, build, .. } =
1789 resolve(&["perl-timedate-git", "perl-mailtools-git"], Flags::new()).await;
1790 assert!(install.is_empty());
1791 assert_eq!(build, ["perl-mailtools-git", "perl-timedate-git"]);
1792
1793 let TestActions { install, build, .. } =
1794 resolve(&["perl-mailtools-git", "perl-timedate-git"], Flags::new()).await;
1795 assert!(install.is_empty());
1796 assert_eq!(build, ["perl-mailtools-git", "perl-timedate-git"]);
1797 }
1798
1799 #[tokio::test]
1800 async fn test_a() {
1801 let TestActions { missing, .. } = resolve(&["a"], Flags::new()).await;
1802
1803 assert_eq!(missing, vec![vec!["a", "b>1"]]);
1804 }
1805
1806 #[tokio::test]
1807 async fn test_a_no_ver() {
1808 let TestActions { build, .. } = resolve(&["a"], Flags::new() | Flags::NO_DEP_VERSION).await;
1809
1810 assert_eq!(build, vec!["a", "b"]);
1811 }
1812
1813 #[tokio::test]
1814 async fn test_discord() {
1815 let TestActions {
1816 make,
1817 install,
1818 build,
1819 ..
1820 } = resolve(&["discord-canary"], Flags::new()).await;
1821
1822 println!("{build:?}");
1823 println!("{install:?}");
1824
1825 assert_eq!(build.len(), 3);
1826 assert_eq!(install.len(), 89 + 13);
1827 assert_eq!(make, 9);
1828 }
1829
1830 #[tokio::test]
1831 async fn test_aur_only() {
1832 let TestActions { build, install, .. } =
1833 resolve(&["xterm", "yay"], Flags::aur_only() | Flags::NO_DEPS).await;
1834 assert_eq!(build, vec!["xterm", "yay-bin"]);
1835 assert_eq!(install, Vec::<String>::new());
1836
1837 let TestActions { install, .. } =
1838 resolve(&["pacman"], Flags::aur_only() | Flags::NO_DEPS).await;
1839 assert_eq!(install, Vec::<String>::new());
1840 }
1842
1843 #[tokio::test]
1844 async fn test_repo_only() {
1845 let TestActions { build, install, .. } = resolve(
1846 &["xterm", "yay"],
1847 (Flags::new() | Flags::NO_DEPS) & !Flags::AUR,
1848 )
1849 .await;
1850 assert_eq!(install, vec!["xterm"]);
1851 assert_eq!(build, Vec::<String>::new());
1852
1853 let TestActions { install, build, .. } =
1854 resolve(&["pacman"], (Flags::new() | Flags::NO_DEPS) & !Flags::AUR).await;
1855 assert_eq!(install, vec!["pacman"]);
1856 assert_eq!(build, Vec::<String>::new());
1857 }
1858
1859 #[tokio::test]
1860 async fn test_dups() {
1861 let TestActions {
1862 build,
1863 install,
1864 duplicates,
1865 ..
1866 } = resolve(&["extra/xterm", "aur/xterm"], Flags::new()).await;
1867
1868 println!("{install:#?}");
1869 println!("{build:#?}");
1870 assert_eq!(duplicates.len(), 1);
1871 }
1872
1873 #[tokio::test]
1874 async fn test_inner_conflicts() {
1875 let alpm = alpm();
1876 let raur = raur();
1877 let mut cache = HashSet::new();
1878 let handle = Resolver::new(&alpm, &mut cache, &raur, Flags::new());
1879 let actions = handle
1880 .resolve_targets(&["yay", "yay-git", "yay-bin"])
1881 .await
1882 .unwrap();
1883
1884 let mut conflict1 = Conflict::new("yay".into());
1885 conflict1.push("yay-git".into(), &Depend::new("yay"));
1886 conflict1.push("yay-bin".into(), &Depend::new("yay"));
1887 let mut conflict2 = Conflict::new("yay-bin".into());
1888 conflict2.push("yay-git".into(), &Depend::new("yay"));
1889 let mut conflict3 = Conflict::new("yay-git".into());
1890 conflict3.push("yay-bin".into(), &Depend::new("yay"));
1891
1892 assert_eq!(
1893 actions.calculate_inner_conflicts(true),
1894 vec![conflict1, conflict2, conflict3]
1895 );
1896 }
1897
1898 #[tokio::test]
1899 async fn test_conflicts() {
1900 let alpm = alpm();
1901 let raur = raur();
1902 let mut cache = HashSet::new();
1903 let handle = Resolver::new(&alpm, &mut cache, &raur, Flags::new());
1904 let actions = handle.resolve_targets(&["pacman-git"]).await.unwrap();
1905
1906 let mut conflict = Conflict::new("pacman-git".into());
1907 conflict.push("pacman".into(), &Depend::new("pacman"));
1908
1909 assert_eq!(actions.calculate_conflicts(true), vec![conflict]);
1910 }
1911
1912 #[tokio::test]
1913 async fn test_aur_updates() {
1914 let alpm = alpm();
1915 let raur = raur();
1916 let mut cache = HashSet::new();
1917 let mut handle = Resolver::new(&alpm, &mut cache, &raur, Flags::new());
1918 let pkgs = handle.updates(None).await.unwrap().aur_updates;
1919 let pkgs = pkgs
1920 .iter()
1921 .map(|p| p.remote.name.as_str())
1922 .collect::<Vec<_>>();
1923
1924 assert_eq!(pkgs, vec!["version_newer"]);
1925 }
1926
1927 #[tokio::test]
1928 async fn test_aur_updates_enable_downgrade() {
1929 let alpm = alpm();
1930 let raur = raur();
1931 let mut cache = HashSet::new();
1932 let mut handle = Resolver::new(
1933 &alpm,
1934 &mut cache,
1935 &raur,
1936 Flags::new() | Flags::ENABLE_DOWNGRADE,
1937 );
1938 let pkgs = handle.updates(None).await.unwrap().aur_updates;
1939 let pkgs = pkgs
1940 .iter()
1941 .map(|p| p.remote.name.as_str())
1942 .collect::<Vec<_>>();
1943
1944 assert_eq!(pkgs, vec!["pacaur", "version_newer", "version_older"]);
1945 }
1946
1947 #[tokio::test]
1948 async fn test_repo_nover() {
1949 let TestActions { install, .. } = resolve(&["repo_version_test"], Flags::new()).await;
1950 assert_eq!(install, Vec::<String>::new());
1951
1952 let TestActions { install, .. } =
1953 resolve(&["repo_version_test"], Flags::new() | Flags::NO_DEP_VERSION).await;
1954 assert_eq!(install, vec!["pacman-contrib"]);
1955 }
1956
1957 #[tokio::test]
1958 async fn test_satisfied_versioned_repo_dep() {
1959 let TestActions { missing, .. } =
1960 resolve(&["satisfied_versioned_repo_dep"], Flags::new()).await;
1961 assert_eq!(
1962 missing,
1963 vec![vec!["satisfied_versioned_repo_dep", "pacman>100"]]
1964 );
1965
1966 let TestActions { missing, .. } = resolve(
1967 &["satisfied_versioned_repo_dep"],
1968 Flags::new() | Flags::NO_DEP_VERSION,
1969 )
1970 .await;
1971 assert_eq!(missing, Vec::<Vec<String>>::new());
1972 }
1973
1974 #[tokio::test]
1975 async fn test_satisfied_versioned_repo_dep_nover() {
1976 let TestActions { build, install, .. } = resolve(
1977 &["satisfied_versioned_repo_dep"],
1978 Flags::new() | Flags::NO_DEP_VERSION,
1979 )
1980 .await;
1981 assert_eq!(build, vec!["satisfied_versioned_repo_dep"]);
1982 assert!(install.is_empty());
1983
1984 let TestActions { missing, .. } = resolve(
1985 &["satisfied_versioned_repo_dep"],
1986 Flags::new() | Flags::NO_DEP_VERSION,
1987 )
1988 .await;
1989 assert_eq!(missing, Vec::<Vec<String>>::new());
1990 }
1991
1992 #[tokio::test]
1993 async fn test_cyclic() {
1994 let TestActions { .. } = resolve(&["cyclic"], Flags::new()).await;
1995 }
1996
1997 #[tokio::test]
1998 async fn test_cyclic2() {
1999 let TestActions { .. } = resolve(&["systemd-git"], Flags::new()).await;
2000 }
2001
2002 #[tokio::test]
2003 async fn test_resolve_targets() {
2004 let raur = raur();
2005 let alpm = alpm();
2007 let mut cache = HashSet::new();
2008 let flags = Flags::new() & !Flags::TARGET_PROVIDES & !Flags::MISSING_PROVIDES;
2009
2010 let handle = Resolver::new(&alpm, &mut cache, &raur, flags).provider_callback(|_, pkgs| {
2011 println!("provider choice: {pkgs:?}");
2012 0
2013 });
2014
2015 let actions = handle
2016 .resolve_targets(&["ros-melodic-desktop-full"])
2017 .await
2018 .unwrap();
2025
2026 actions
2027 .iter_aur_pkgs()
2028 .for_each(|p| println!("b {}", p.pkg.name));
2029 actions
2030 .iter_pkgbuilds()
2031 .for_each(|p| println!("c {}", p.1.pkg.pkgname));
2032
2033 actions
2034 .install
2035 .iter()
2036 .for_each(|p| println!("i {}", p.pkg.name()));
2037
2038 actions
2039 .missing
2040 .iter()
2041 .for_each(|m| println!("missing {m:?}"));
2042
2043 actions.calculate_conflicts(true).iter().for_each(|c| {
2044 println!("c {}: ", c.pkg);
2045 c.conflicting
2046 .iter()
2047 .for_each(|c| println!(" {} ({:?})", c.pkg, c.conflict))
2048 });
2049
2050 actions
2051 .calculate_inner_conflicts(true)
2052 .iter()
2053 .for_each(|c| {
2054 println!("c {}: ", c.pkg);
2055 c.conflicting
2056 .iter()
2057 .for_each(|c| println!(" {} ({:?})", c.pkg, c.conflict))
2058 });
2059
2060 actions
2061 .unneeded
2062 .iter()
2063 .for_each(|p| println!("u {}", p.name));
2064
2065 actions
2066 .duplicate_targets()
2067 .iter()
2068 .for_each(|p| println!("d {p}"));
2069
2070 println!(
2071 "build: {}",
2072 actions.iter_aur_pkgs().count() + actions.iter_pkgbuilds().count()
2073 );
2074
2075 println!("install: {}", actions.install.len());
2076 }
2077
2078 #[tokio::test]
2079 async fn test_target_flags() {
2080 let TestActions { targets, .. } = resolve(&["discord-canary"], Flags::new()).await;
2081 assert_eq!(targets, vec!["discord-canary"]);
2082 }
2083}