1# ex:ts=8 sw=4:
2# $OpenBSD: Dependencies.pm,v 1.4 2005/10/10 11:24:34 espie Exp $
3#
4# Copyright (c) 2005 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
18use strict;
19use warnings;
20
21package OpenBSD::Dependencies;
22
23use OpenBSD::PackageName;
24use OpenBSD::PkgSpec;
25use OpenBSD::PackageInfo;
26use OpenBSD::SharedLibs;
27use OpenBSD::Error;
28use OpenBSD::Interactive;
29
30sub solve
31{
32	my ($state, $handle, @extra) = @_;
33	my $plist = $handle->{plist};
34	my $verbose = $state->{verbose};
35	my $to_register = $handle->{solved_dependencies} = {};
36	my $to_install = {};
37	for my $fullname (@extra) {
38		$to_install->{OpenBSD::PackageName::url2pkgname($fullname)} =
39		    $fullname;
40	}
41	my @avail;
42
43	# do simple old style pkgdep first
44	my @deps = ();
45	for my $dep (@{$plist->{pkgdep}}) {
46		if (!is_installed($dep->{name})) {
47			push(@deps, $dep->{name});
48		}
49		$to_register->{$dep->{name}} = 1;
50	}
51	for my $dep (@{$plist->{depend}}, @{$plist->{newdepend}}, @{$plist->{libdepend}}) {
52	    next if defined $dep->{name} and $dep->{name} ne $plist->pkgname();
53
54	    my @candidates;
55	    if ($state->{replace}) {
56		# try against list of packages to install
57		@candidates = OpenBSD::PkgSpec::match($dep->{pattern}, keys %{$to_install});
58		if (@candidates >= 1) {
59		    push(@deps, $to_install->{$candidates[0]});
60		    $to_register->{$candidates[0]} = 1;
61		    next;
62		}
63	    }
64	    @candidates = OpenBSD::PkgSpec::match($dep->{pattern}, installed_packages());
65	    if (@candidates >= 1) {
66		    $to_register->{$candidates[0]} = 1;
67		    next;
68	    }
69	    if (!$state->{replace}) {
70		# try against list of packages to install
71		@candidates = OpenBSD::PkgSpec::match($dep->{pattern}, keys %{$to_install});
72		if (@candidates >= 1) {
73		    push(@deps, $to_install->{$candidates[0]});
74		    $to_register->{$candidates[0]} = 1;
75		    next;
76		}
77	    }
78	    if (!@avail) {
79	    	@avail = OpenBSD::PackageLocator::available();
80		if (!$state->{forced}->{allversions}) {
81		    @avail = OpenBSD::PackageName::keep_most_recent(@avail);
82		}
83	    }
84	    # try with list of available packages
85	    @candidates = OpenBSD::PkgSpec::match($dep->{pattern}, @avail);
86	    # one single choice
87	    if (@candidates == 1) {
88		push(@deps, $candidates[0]);
89		$to_register->{$candidates[0]} = 1;
90		next;
91	    }
92	    if (@candidates > 1) {
93		# put default first if available
94		@candidates = ((grep {$_ eq $dep->{def}} @candidates),
95				(sort (grep {$_ ne $dep->{def}} @candidates)));
96		my $choice =
97		    OpenBSD::Interactive::ask_list('Choose dependency for '.$plist->pkgname().': ',
98			$state->{interactive}, @candidates);
99		push(@deps, $choice);
100		$to_register->{$choice} = 1;
101		next;
102	    }
103	    # can't get a list of packages, assume default
104	    # will be there.
105	    push(@deps, $dep->{def});
106	    $to_register->{$dep->{def}} = 1;
107	}
108
109	if ($verbose && %$to_register) {
110	    print "Dependencies for ", $plist->pkgname(), " resolve to: ",
111	    	join(', ', keys %$to_register);
112	    print " (todo: ", join(',', @deps), ")" if @deps > 0;
113	    print "\n";
114	}
115	return @deps;
116}
117
118sub check_lib_spec
119{
120	my ($base, $spec, $dependencies) = @_;
121	my @r = OpenBSD::SharedLibs::lookup_libspec($base, $spec);
122	for my $candidate (@r) {
123		if ($dependencies->{$candidate}) {
124			return $candidate;
125		}
126	}
127	return undef;
128}
129
130sub find_old_lib
131{
132	my ($state, $base, $pattern, $lib, $dependencies) = @_;
133
134	$pattern = ".libs-".$pattern;
135	for my $try (OpenBSD::PkgSpec::match($pattern, installed_packages())) {
136		OpenBSD::SharedLibs::add_package_libs($try);
137		if (check_lib_spec($base, $lib, {$try => 1})) {
138			$dependencies->{$try} = 1;
139			return "$try($lib)";
140		}
141	}
142	return undef;
143}
144
145sub lookup_library
146{
147	my ($state, $lib, $plist, $dependencies, $harder, $done) = @_;
148
149	my $r = check_lib_spec($plist->pkgbase(), $lib, $dependencies);
150	if ($r) {
151	    print "found libspec $lib in $r\n" if $state->{very_verbose};
152	    return 1;
153	}
154	if ($harder && $lib !~ m|/|) {
155
156		OpenBSD::SharedLibs::add_system_libs($state->{destdir});
157		if (check_lib_spec("/usr", $lib, {system => 1})) {
158			print "found libspec $lib in /usr\n" if $state->{very_verbose};
159			return 1;
160		}
161		if (check_lib_spec("/usr/X11R6", $lib, {system => 1})) {
162			print "found libspec $lib in /usr/X11R6\n" if $state->{very_verbose};
163			return 1;
164		}
165	}
166	for my $dep (@{$plist->{depends}}) {
167		$r = find_old_lib($state, $plist->pkgbase(), $dep->{pattern}, $lib, $dependencies);
168		if ($r) {
169			print "found libspec $lib in old package $r\n" if $state->{verbose};
170			return 1;
171		}
172    	}
173	if ($harder) {
174		# lookup through the full tree...
175		my @todo = keys %$dependencies;
176		while (my $dep = pop @todo) {
177			require OpenBSD::RequiredBy;
178
179			next if $done->{$dep};
180			$done->{$dep} = 1;
181			for my $dep2 (OpenBSD::Requiring->new($dep)->list()) {
182				push(@todo, $dep2) unless $done->{$dep2};
183			}
184			next if $dependencies->{$dep};
185			OpenBSD::SharedLibs::add_package_libs($dep);
186			if (check_lib_spec($plist->pkgbase(), $lib, {$dep => 1})) {
187				print "found libspec $lib in dependent package $dep\n" if $state->{verbose};
188				$dependencies->{$dep} = 1;
189				return 1;
190			}
191		}
192	}
193	if ($state->{forced}->{boguslibs}) {
194		my $explored = {};
195		# lookup through the full tree...
196		my @todo = keys %$dependencies;
197		while (my $dep = pop @todo) {
198			require OpenBSD::RequiredBy;
199
200			next if $explored->{$dep};
201			$explored->{$dep} = 1;
202			for my $dep2 (OpenBSD::Requiring->new($dep)->list()) {
203				push(@todo, $dep2) unless $done->{$dep2};
204			}
205			OpenBSD::SharedLibs::add_bogus_package_libs($dep);
206			if (check_lib_spec($plist->pkgbase(), $lib, {$dep => 1})) {
207				print "found libspec $lib in dependent package $dep (unmarked library)\n" if $state->{verbose};
208				$dependencies->{$dep} = 1;
209				return 1;
210			}
211		}
212	}
213	print "libspec $lib not found\n" if $state->{very_verbose};
214	return;
215}
216
217
2181;
219