1package Magus::Index;
2#
3# Copyright (c) 2007,2008 Chris Reinhardt. All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# 1. Redistributions of source code must retain the above copyright notice
10#    this list of conditions and the following disclaimer.
11#
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
17# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27
28#
29# MAINTAINER=   ctriv@MidnightBSD.org
30#
31
32use strict;
33use warnings;
34
35use Mport::Utils qw(make_var recurse_ports);
36
37use YAML qw(Load);
38
39sub sync {
40  my ($class, $root, $run) = @_;
41  my $arch = $run->arch;
42  my $osrel = $run->osversion;
43  my $osversion;
44  my %visited;
45
46  if ($osrel eq "4.1") {
47    $osversion = 401000;
48  } elsif ($osrel eq "4.0") {
49    $osversion = 400002;
50  } elsif ($osrel eq "3.2") {
51    $osversion = 302001;
52  } elsif ($osrel eq "3.1") {
53    $osversion = 301000;
54  } elsif ($osrel eq "3.0") {
55    $osversion = 300005;
56  } elsif ($osrel eq "2.2") {
57    $osversion = 202000;
58  } elsif ($osrel eq "2.1") {
59    $osversion = 201001;
60  } elsif ($osrel eq "2.0") {
61    $osversion = 200000;
62  } elsif ($osrel eq "1.3") {
63    $osversion = 103000;
64  } elsif ($osrel eq "1.2") {
65    $osversion = 102000;
66  } elsif ($osrel eq "1.1") {
67    $osversion = 101000;
68  } elsif ($osrel eq "1.0") {
69    $osversion = 100000;
70  } else {
71    $osversion = 500000;
72  }
73
74  $root ||= "$Magus::Config{MasterDataDir}/$Magus::Config{MportsVcsDir}";
75
76  local $| = 1;
77
78  my %depends;
79
80  recurse_ports {
81    print @_, "... ";
82
83    my $yaml = `__MAKE_CONF=/dev/null SSL_DEFAULT=base INDEXING=1 ARCH=$arch OSREL=$osrel OSVERSION=$osversion PORTSDIR=$root BATCH=1 PACKAGE_BUILDING=1 MAGUS=1 make describe-yaml`;
84    my %dump;
85
86    eval {
87      %dump = %{ Load($yaml) };
88    };
89
90    if ($@) {
91      warn "Unable to parse yaml for $_[0]: $@\n";
92      return;
93    }
94
95    my $primaryFlavor = $dump{flavor};
96    my $defaultFlavor = 0;
97
98    my $port = Magus::Port->insert({
99      run         => $run,
100      name        => $dump{name},
101      version     => $dump{version},
102      description => $dump{description},
103      license	  => join(" ", @{$dump{'license'}}),
104      restricted  => $dump{restricted},
105      www         => $dump{www},
106      pkgname     => $dump{pkgname},
107      flavor      => $dump{flavor},
108      cpe         => $dump{cpe},
109      default_flavor => 1,
110    });
111
112     for (@{$dump{'master_sites'}}) {
113       Magus::MasterSite->insert({
114           port => $port->id,
115           url => $_
116       });
117     }
118
119     for (@{$dump{'distfiles'}}) {
120       Magus::Distfile->insert({
121           port      => $port->id,
122           filename => $_
123       });
124     }
125
126     for (@{$dump{'restricted_distfiles'}}) {
127       Magus::RestrictedDistfile->insert({
128           port      => $port->id,
129           filename => $_
130       });
131     }
132
133
134     for (@{$dump{'license_perms'}}) {
135       Magus::PortLicensePerms->insert({
136           port      => $port->id,
137           perm => $_
138       });
139     }
140
141    $depends{$port->id} = [];
142    while (my ($type, $deps) = each %{$dump{'depends'}}) {
143      foreach my $dep (@$deps) {
144	my %dependsItem;
145	my @deporigin = split /@/, $dep;
146	$dependsItem{name} = $deporigin[0];
147	$dependsItem{type} = $type;
148	my $len = @deporigin;
149	if ($len > 1) {
150	  $dependsItem{flavor} = $deporigin[1];
151	} else {
152	  $dependsItem{flavor} = "";
153        }
154	push(@{$depends{$port->id}}, \%dependsItem);
155      }
156    }
157
158    $class->sync_categories(\%dump, $port, $arch);
159
160    $class->mark_ignored(\%dump, $port);
161
162   foreach my $flav (@{$dump{'flavors'}}) {
163     print "Flavor: $flav\n";
164     if ($flav eq $primaryFlavor) {
165       print "Default flavor $flav processed.\n";
166       next;
167     }
168     $yaml = `__MAKE_CONF=/dev/null SSL_DEFAULT=base INDEXING=1 ARCH=$arch OSREL=$osrel OSVERSION=$osversion PORTSDIR=$root BATCH=1 PACKAGE_BUILDING=1 MAGUS=1 make describe-yaml FLAVOR=$flav`;
169    eval {
170      %dump = %{ Load($yaml) };
171    };
172      if ($@) {
173        warn "Unable to parse yaml for $_[0]: $@\n";
174        next;
175      }
176
177	$port = Magus::Port->insert({
178      run         => $run,
179      name        => $dump{name},
180      version     => $dump{version},
181      description => $dump{description},
182      license     => join(" ", @{$dump{'license'}}),
183      restricted  => $dump{restricted},
184      www         => $dump{www},
185      pkgname     => $dump{pkgname},
186      flavor      => $dump{flavor},
187      default_flavor => 0,
188    });
189
190     for (@{$dump{'master_sites'}}) {
191       Magus::MasterSite->insert({
192           port => $port->id,
193           url => $_
194       });
195     }
196
197     for (@{$dump{'distfiles'}}) {
198       Magus::Distfile->insert({
199           port      => $port->id,
200           filename => $_
201       });
202     }
203
204     for (@{$dump{'restricted_distfiles'}}) {
205       Magus::RestrictedDistfile->insert({
206           port      => $port->id,
207           filename => $_
208       });
209     }
210
211    $depends{$port->id} = [];
212    while (my ($type, $deps) = each %{$dump{'depends'}}) {
213      foreach my $dep (@$deps) {
214        my %dependsItem;
215	my @deporigin = split /@/, $dep;
216        $dependsItem{name} = $deporigin[0];
217        $dependsItem{type} = $type;
218        my $len = @deporigin;
219        if ($len > 1) {
220          $dependsItem{flavor} = $deporigin[1];
221        } else {
222          $dependsItem{flavor} = "";
223        }
224        push(@{$depends{$port->id}}, \%dependsItem);
225      }
226    }
227
228    $class->sync_categories(\%dump, $port, $arch);
229
230    $class->mark_ignored(\%dump, $port);
231   }
232
233    print "done.\n";
234  } root    => $root, nochdir => sub { warn "ERROR: no such port $_[0]\n" };
235
236  print "Building depends list... \n";
237
238  PORT: while (my ($id, $depends) = each %depends) {
239    my $port = Magus::Port->retrieve($id) || die "Got an invalid port in the depends list! ($id)";
240    my %seen_depends;
241
242    foreach my $item (@$depends) {
243      my $fl = $item->{flavor};
244      if (length $fl < 1) {
245        $fl = "";
246      }
247      my $depend = Magus::Port->retrieve(run => $run, name => $item->{name}, flavor => $fl);
248
249      if (!defined($depend) && length $fl) {
250        warn "\tMissing flavor for $port: $item->{name} with flavor: $fl. Trying no flavor.\n";
251        $depend = Magus::Port->retrieve(run => $run, name => $item->{name}, flavor => "");
252      }
253
254      if (!defined($depend)) {
255	warn "\tMissing flavor for $port: $item->{name} , falling back to default flavor.\n";
256        $depend = Magus::Port->retrieve(run => $run, name => $item->{name}, default_flavor => 1);
257      }
258
259      if (!defined($depend)) {
260        warn "\tMissing depend for $port: $item->{name}\n";
261        $port->set_result_fail(qq(depend "$item->{name}" with flavor: "$fl" does not exist.));
262        next PORT;
263      }
264
265      my $depend_key = join(":", $depend->id, $item->{type});
266      if ($seen_depends{$depend_key}) {
267        warn "\tBad depends for $port: duplicate $item->{type} dependency "
268          . "$item->{name}" . (length $fl ? "\@$fl" : "")
269          . " resolves to " . $depend->name . " with flavor \""
270          . ($depend->flavor // "") . "\". Skipping duplicate.\n";
271        next;
272      }
273      $seen_depends{$depend_key} = 1;
274
275      $port->add_to_depends({
276        dependency => $depend,
277	type => $item->{type}
278      });
279    }
280  }
281
282  print "Building MOVED list... \n";
283  moved_list($run, $root);
284
285  print "done.\n";
286}
287
288sub moved_list {
289  my ($run, $root) = @_;
290
291  my $fh;
292  my %result;
293  open($fh, $root . "/MOVED") || die "failed to read MOVED file: $!";
294  while (my $line = <$fh>) {
295    chomp $line;
296    # skip comments and blank lines
297    next if $line =~ /^\#/ || $line =~ /^\s*$/ || $line =~ /^\+/;
298    #split each line into array
299    my ($port, $moved_to, $date, $why) = split(/\|/, $line);
300
301    next unless defined $port;
302
303    print "Adding MOVED entry: $port $moved_to $date $why \n";
304
305    if (defined($date) && length($date)) {
306	  $port = Magus::Moved->insert({
307      run         => $run,
308      port => $port,
309      moved_to => $moved_to,
310      date => $date,
311      why => $why,
312    });
313    } else  {
314      $port = Magus::Moved->insert({
315	run  => $run,
316	port => $port,
317	moved_to => $moved_to,
318	date => undef,
319	why => $why,
320      });
321    }
322  }
323}
324
325
326sub sync_categories {
327    my ($class, $dump, $port, $arch) = @_;
328
329    Magus::PortCategory->search(port => $port)->delete_all;
330
331    for (@{$dump->{'categories'}}) {
332      my $cat = Magus::Category->find_or_create({ category => $_});
333      $port->add_to_categories({ category => $cat });
334    }
335}
336
337sub mark_ignored {
338    my ($class, $dump, $port) = @_;
339    my $ignore = $dump->{ignore} // "";
340
341    if ($class->ignore_is_metaport($ignore)) {
342      print "\n\tMetaport.  Marking as internal.";
343      $port->set_result_internal($ignore);
344    } elsif ($dump->{is_interactive}) {
345      print "\n\tIGNORE set.  Marking as skipped.";
346      $port->set_result_skip("Port is marked as interactive.");
347    } elsif (length $ignore) {
348      print "\n\tIGNORE set.  Marking as skipped.";
349      $port->set_result_skip($ignore);
350    }
351}
352
353sub ignore_is_metaport {
354    my ($class, $ignore) = @_;
355
356    return defined($ignore) && $ignore =~ /\bis a meta[- ]?port; there is nothing to build\b/;
357}
358
3591;
360__END__
361
362