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