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