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