xref: /openbsd-src/usr.sbin/pkg_add/OpenBSD/Tracker.pm (revision 8773e53f4ac2617fa45ad0e0a467e36b1d94e0ce)
1# ex:ts=8 sw=4:
2# $OpenBSD: Tracker.pm,v 1.33 2023/10/08 09:16:39 espie Exp $
3#
4# Copyright (c) 2009 Marc Espie <espie@openbsd.org>
5#
6# Permission to use, copy, modify, and distribute this software for any
7# purpose with or without fee is hereby granted, provided that the above
8# copyright notice and this permission notice appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
17# In order to deal with dependencies, we have to know what's actually installed,
18# and what can actually be updated.
19# Specifically, to solve a dependency:
20# - look at packages to_install
21# - look at installed packages
22#   - if it's marked to_update, then we must process the update first
23#   - if it's marked as installed, or as cant_update, or uptodate, then
24#   we can use the installed packages.
25#   - otherwise, in update mode, put a request to update the package (e.g.,
26#   create a new UpdateSet.
27
28# the Tracker object does maintain that information globally so that
29# Update/Dependencies can do its job.
30
31use v5.36;
32use warnings;
33
34package OpenBSD::Tracker;
35
36# XXX we're a singleton class
37our $s;
38
39sub new($class)
40{
41	return $s //= bless {}, $class;
42}
43
44sub dump2($set)
45{
46	if (defined $set->{merged}) {
47		return "merged from ".dump2($set->{merged});
48	}
49	return join("/",
50	    join(",", $set->newer_names),
51	    join(",", $set->older_names),
52	    join(",", $set->kept_names),
53	    join(",", $set->hint_names));
54}
55
56sub dump($)
57{
58	return unless defined $s;
59	for my $l ('to_install', 'to_update') {
60		next unless defined $s->{$l};
61		print STDERR "$l:\n";
62		while (my ($k, $e) = each %{$s->{$l}}) {
63			print STDERR "\t$k => ", dump2($e), "\n";
64		}
65	}
66	for my $l ('uptodate', 'can_install', 'cant_update') {
67		next unless defined $s->{$l};
68		print STDERR "$l: ", join(' ', keys %{$s->{$l}}), "\n";
69	}
70}
71
72sub sets_todo($self, $offset = 0)
73{
74	return sprintf("%u/%u", (scalar keys %{$self->{done}})-$offset,
75		scalar keys %{$self->{total}});
76}
77
78sub handle_set($self, $set)
79{
80	$self->{total}{$set} = 1;
81	if ($set->{finished}) {
82		$self->{done}{$set} = 1;
83	}
84}
85
86sub known($self, $set)
87{
88	for my $n ($set->newer, $set->older, $set->hints) {
89		$self->{known}{$n->pkgname} = 1;
90	}
91}
92
93sub add_set($self, $set)
94{
95	for my $n ($set->newer) {
96		$self->{to_install}{$n->pkgname} = $set;
97	}
98	for my $n ($set->older, $set->hints) {
99		$self->{to_update}{$n->pkgname} = $set;
100	}
101	for my $n ($set->kept) {
102		delete $self->{to_update}{$n->pkgname};
103		$self->{uptodate}{$n->pkgname} = 1;
104		if ($n->{is_firmware}) {
105			$self->{firmware}{$n->pkgname} = 1;
106		}
107	}
108	$self->known($set);
109	$self->handle_set($set);
110	return $self;
111}
112
113sub todo($self, @sets)
114{
115	for my $set (@sets) {
116		$self->add_set($set);
117	}
118	return $self;
119}
120
121sub remove_set($self, $set)
122{
123	for my $n ($set->newer) {
124		delete $self->{to_install}{$n->pkgname};
125		delete $self->{cant_install}{$n->pkgname};
126	}
127	for my $n ($set->kept, $set->older, $set->hints) {
128		delete $self->{to_update}{$n->pkgname};
129		delete $self->{cant_update}{$n->pkgname};
130	}
131	$self->handle_set($set);
132}
133
134sub uptodate($self, $set)
135{
136	$set->{finished} = 1;
137	$self->remove_set($set);
138	for my $n ($set->older, $set->kept) {
139		$self->{uptodate}{$n->pkgname} = 1;
140		if ($n->{is_firmware}) {
141			$self->{firmware}{$n->pkgname} = 1;
142		}
143	}
144}
145
146sub cant($self, $set)
147{
148	$set->{finished} = 1;
149	$self->remove_set($set);
150	$self->known($set);
151	for my $n ($set->older) {
152		$self->{cant_update}{$n->pkgname} = 1;
153	}
154	for my $n ($set->newer) {
155		$self->{cant_install}{$n->pkgname} = 1;
156	}
157	for my $n ($set->kept) {
158		$self->{uptodate}{$n->pkgname} = 1;
159	}
160}
161
162sub done($self, $set)
163{
164	$set->{finished} = 1;
165	$self->remove_set($set);
166	$self->known($set);
167
168	for my $n ($set->newer) {
169		$self->{uptodate}{$n->pkgname} = 1;
170		$self->{installed}{$n->pkgname} = 1;
171	}
172	for my $n ($set->kept) {
173		$self->{uptodate}{$n->pkgname} = 1;
174	}
175}
176
177sub is($self, $k, $pkg)
178{
179	my $set = $self->{$k}{$pkg};
180	if (ref $set) {
181		return $set->real_set;
182	} else {
183		return $set;
184	}
185}
186
187sub is_known($self, $pkg)
188{
189	return $self->is('known', $pkg);
190}
191
192sub is_installed($self, $pkg)
193{
194	return $self->is('installed', $pkg);
195}
196
197sub is_to_update($self, $pkg)
198{
199	return $self->is('to_update', $pkg);
200}
201
202sub cant_list($self)
203{
204	return keys %{$self->{cant_update}};
205}
206
207sub did_something($self)
208{
209	for my $k (keys %{$self->{uptodate}}) {
210		next if $self->{firmware}{$k};
211		return 1;
212	}
213	return 0;
214}
215
216sub cant_install_list($self)
217{
218	return keys %{$self->{cant_install}};
219}
220
2211;
222