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