[backend] syntax fix of last commit
[opensuse:build-service.git] / src / backend / bs_productconvert
1 #!/usr/bin/perl -w
2 #
3 # Copyright (c) 2008 Klaas Freitag, Novell Inc.
4 # Copyright (c) 2008 Adrian Schroeter, Novell Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License version 2 as
8 # published by the Free Software Foundation.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program (see the file COPYING); if not, write to the
17 # Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 #
20 ################################################################
21 #
22 # Converter to create Kiwi- and Spec files from product definition
23 #
24 BEGIN {
25   my ($wd) = $0 =~ m-(.*)/- ;
26   $wd ||= '.';
27   unshift @INC,  "$wd";
28 }
29
30 use strict;
31 use Getopt::Std;
32 use Data::Dumper;
33 use File::Basename;
34 use Storable;
35
36 use XML::Structured ':bytes';
37 use BSUtil;
38 use BSXML;
39 use BSProductXML;
40 use BSKiwiXML;
41 use BSXML;
42
43 my $bsdir;
44 my $localarch;
45 my $obsname;
46 eval{
47   require BSConfig;
48   $bsdir = "$BSConfig::bsdir" if defined($BSConfig::bsdir);
49   $localarch = "$BSConfig::localarch" if defined($BSConfig::localarch);
50   $obsname = "$BSConfig::obsname" if defined($BSConfig::obsname);
51 };
52
53
54 # read the product xml file
55
56 use vars qw ( $opt_h $opt_l $opt_d $opt_m 
57               @errors %conditionals %repositories %groupRefs %archSets $indir $runOnServer);
58
59 my %product_requires;
60
61 sub usage()
62 {
63   print<<END
64
65   bs_productconvert product_file output_directory [project_name]
66
67   convert a product definition file to a spec- and a kiwi source file.
68
69   Options:
70
71   -h:   help, print this text.
72   -l:   Run local on server, use direct path instead of obs:// URL.
73   -d:   debug, create debug output and files
74   -m:   mediaset, build only for the given mediaset, identify by name
75 END
76 ;
77
78   exit;
79 }
80
81 sub readProductFile( $ )
82 {
83     my ($filename) = @_;
84
85     print "parsing product definition... ";
86     my $xml = BSProductXML::readproductxml( "$filename", 0, $opt_d );
87
88     if( defined($xml) ) {
89         print "success!\n";
90         # print Dumper $xml;
91     } else {
92         print "FAILED: Unable to parse $filename\n";
93         die;
94     }
95     return $xml;
96 }
97
98
99 sub createDescription( $ )
100 {
101     my( $prodRef ) = @_;
102     my $re;
103
104     $re->{type} = "system";
105     $re->{author} = "The SUSE Team";
106     $re->{contact} = "build\@opensuse.org";
107     $re->{specification} = $prodRef->{summary}[0]->{_content};  # FIXME: lang dependent
108
109     return $re;
110 }
111
112 sub convertFlags( $ ){
113     my ($flag)=@_;
114     $flag =~ s/GT/>/sg;
115     $flag =~ s/EQ/=/sg;
116     $flag =~ s/LT/</sg;
117     $flag =~ s/NE/!=/sg;
118     $flag =~ s/GE/>=/sg;
119     $flag =~ s/LE/<=/sg;
120     return $flag;
121 }
122
123 sub convertRelationship( $ ){
124     my ( $relationship ) = @_;
125     $relationship =~ s/suggests/Suggests/sg;
126     $relationship =~ s/recommends/Recommends/sg;
127     $relationship =~ s/requires/Requires/sg;
128     $relationship =~ s/provides/Provides/sg;
129     $relationship =~ s/conflicts/Conflicts/sg;
130     $relationship =~ s/obsoletes/Obsoletes/sg;
131     return $relationship;
132 }
133
134 #
135 # The conditionals are kind of macros which are used all over the product definition.
136 # The conditionals part of the product def is parsed into the global conditionalhash
137 # with the conditional name as key.
138 #
139 sub parseConditionals( $ )
140 {
141     my ($conditionalRef) = @_;
142     # print Dumper $conditionalRef;
143     return unless( $conditionalRef );
144
145     foreach my $condRef (@{$conditionalRef}) {
146         my $name = $condRef->{name};
147 #       print "Parsed conditional $name\n";
148         # print Dumper $condRef;
149         $conditionals{$name} = $condRef;
150     }
151 }
152
153 sub parseArchsets( $ ) 
154 {
155     my ($archSetsRef ) = @_;
156
157     foreach my $archSet ( @{$archSetsRef } ) {
158         # print "Parsing Archset $archSet->{name}\n";
159
160         # print "XXXX " . Dumper( $archSet ) . "\n";
161         if( $archSet->{name} ) {
162             my %h;
163             $h{productarch} = $archSet->{productarch};
164             my @a;
165             foreach my $cRef ( @{$archSet->{arch}} ) {
166                 push @a, $cRef->{_content};
167             }
168             $h{archList} = \@a;
169             $archSets{$archSet->{name}} = \%h;
170         }
171     }
172     # print Dumper %archSets;
173 }
174
175 sub getUrl( $$$ ){
176     my ($product,$arch,$searchstring) = @_;
177     my $url="";
178     foreach my $url ( @{$product->{'urls'}->{'url'}} ){
179         if ( "$url->{'name'}" eq "$searchstring" ){
180             my $url = $url->{'_content'};
181             $url =~ s/%{_target_cpu}/$arch/g;
182             return $url;
183         }
184     }
185     return $url;
186 }
187
188 sub createArchitectures( $ )
189 {
190     my ($archSetList) = @_;
191
192     my $re = {};
193
194     my @archs;
195     my %reqArchs;
196
197     my %archMatrix;
198
199     foreach my $requiredArch( @{$archSetList} ) {
200         my $ref = $requiredArch->{ref};
201         die ( "ERROR: No such archset $requiredArch\n" ) unless $archSets{$ref} ;
202         my @archis = @{ $archSets{$ref}->{archList} };
203         my $border = @archis; # the amount of entries
204         
205         print "WARN: last arch in archset must be noarch\n" unless( $archis[$border-1] eq "noarch" );
206
207         $reqArchs{ $archSets{$ref}->{productarch} } = 1; # will be requiredarch in kiwi
208         
209         for( my $i = 0; $i < $border; $i++ ) {
210             $archMatrix{ $archis[$i] } = { fallback => $archis[$i+1] };
211         }
212     }
213     foreach my $arch ( sort keys %archMatrix ) {
214         my %h;
215         $h{id} = $arch;
216         if( $archMatrix{$arch}->{name} ) {
217             $h{name} = $archMatrix{$arch}->{name};
218         } else {
219             $h{name} = "dummy"; # FIXME: should become optional
220         };
221         $h{fallback} = $archMatrix{$arch}->{fallback} if( $archMatrix{$arch}->{fallback});
222         push @archs, \%h;
223     }
224     
225     my @reqXml;
226
227     foreach ( sort keys %reqArchs ) {
228         my %h;
229         $h{ref} = $_;
230         push @reqXml, \%h;
231     }
232     $re->{arch} = \@archs;
233     $re->{requiredarch} = \@reqXml;
234     
235     return $re;
236 }
237
238 sub createProductOptions($$$)
239 {
240     my( $prodRef, $medium, $archSetList ) = @_;
241
242     # check for the wanted media style
243     my $mediaStyle = "suse-11.1"; # fallback value
244     $mediaStyle = $medium->{'mediastyle'} if (defined($medium->{'mediastyle'}));
245     $mediaStyle = "suse-11.3" if defined($medium->{'mediastyle'}) && $medium->{'mediastyle'} eq "suse-11.4"; # there is no kiwi plugin for 11.4
246
247     # General FIXME: this works only with a single product on media.
248     my $product = $prodRef->{products}{product}[0];
249     die( "Handling of multiple products on one media is currently not specified !\n" ) if $prodRef->{products}{product}[1];
250
251     my $re = {};
252     my %varsH;
253     # we need the "default" arch for 
254     # - MEDIUM_NAME
255     # - releasenotesurl
256     my $arch="i586";
257     my @allarchs;
258     foreach my $ar ( @$archSetList ) {
259         $arch=$archSets{$ar->{ref}}->{productarch} if ($archSets{$ar->{ref}});
260         push @allarchs, $arch;
261     }
262
263     $varsH{MEDIUM_NAME} = $product->{'name'}."-".$product->{'version'}."-".join( "-", @allarchs );
264     if ( defined $medium->{'name'} ) {
265       $varsH{MEDIUM_NAME} = $medium->{'name'}."-".join( "-", @allarchs );
266     }
267     $varsH{PRODUCT_THEME} = $product->{'buildconfig'}->{'producttheme'};
268     $varsH{MULTIPLE_MEDIA} = "no";
269     $varsH{MULTIPLE_MEDIA} = "true" if (defined($medium->{'sourcemedia'}) && $medium->{'sourcemedia'} > 1);
270     $varsH{MULTIPLE_MEDIA} = "true" if (defined($medium->{'debugmedia'}) && $medium->{'debugmedia'} > 1);
271     $varsH{RUN_MEDIA_CHECK} = "true" if (defined($medium->{'run_media_check'})) && $medium->{'run_media_check'} ne "no" && $medium->{'run_media_check'} eq "false";
272     $varsH{RUN_ISOHYBRID} = "true" if (defined($medium->{'run_hybridiso'})) && $medium->{'run_hybridiso'} eq "true";
273     $varsH{CREATE_REPOMD} = "true" if (defined($medium->{'create_repomd'})) && $medium->{'create_repomd'} ne "no" && $medium->{'create_repomd'} ne "false";
274     $varsH{MAKE_LISTINGS} = 'false' if (defined($medium->{'run_make_listings'})) && $medium->{'run_make_listings'} ne "yes" && $medium->{'run_make_listings'} ne "true";
275     $varsH{REPO_ONLY} = "true" if (defined($medium->{'repo_only'})) && $medium->{'repo_only'} ne "no" && $medium->{'repo_only'} ne "false";
276     $varsH{SHA1OPT} = "-x";
277     # switch to sha256 sums except for old distros
278     $varsH{SHA1OPT} .= " -2" unless $mediaStyle eq "suse-11.1" || $mediaStyle eq "suse-11.2" || $mediaStyle eq "suse-11.3" || $mediaStyle eq "suse-sle11-sp2";
279
280     $varsH{VENDOR} = $product->{'vendor'};
281     $varsH{DISTNAME} = $product->{'name'};
282     $varsH{VERSION} = $product->{'version'};
283     $varsH{FLAVOR} = $medium->{'flavor'};
284     $varsH{PRODUCT_DIR} = "/";
285     $varsH{PRODUCT_NAME} = '$DISTNAME-$FLAVOR';
286     $varsH{PRODUCT_VERSION} = '$VERSION';
287
288     my @vars;
289     foreach my $opt ( sort keys %varsH ) {
290         push @vars, { name => $opt, _content => $varsH{$opt} };
291     }
292
293     $re->{productvar} = \@vars;
294
295     my %options;
296     if (defined($medium->{'sourcemedia'})) {
297       $options{'SOURCEMEDIUM'} = $medium->{'sourcemedia'};
298     };
299     if (defined($medium->{'debugmedia'})) {
300       $options{'DEBUGMEDIUM'} = $medium->{'debugmedia'};
301     };
302     
303     $options{'IGNORE_MISSING_REPO_PACKAGES'} = "true" if (defined($medium->{'ignore_missing_packages'}) && $medium->{'ignore_missing_packages'} eq "true");
304     $options{'IGNORE_MISSING_META_PACKAGES'} = "true" if (defined($medium->{'ignore_missing_meta_packages'}) && $medium->{'ignore_missing_meta_packages'} eq "true");
305     $options{'PLUGIN_DIR'} = "/usr/share/kiwi/modules/plugins/$mediaStyle";
306     $options{'INI_DIR'} = "/usr/share/kiwi/modules/plugins/$mediaStyle";
307     $options{'BETA_VERSION'} = $product->{'buildconfig'}->{'betaversion'} if (defined($product->{'buildconfig'}->{'betaversion'}));
308
309     my %info;
310     $info{'VENDOR'}       = $product->{'vendor'};
311     $info{'NAME'}         = $product->{'name'};
312
313     # set strings
314     $info{'VERSION'}      = $product->{'version'};
315     $info{'SP_VERSION'}   = $product->{'patchlevel'} if (defined($product->{'patchlevel'}));
316     $info{'DISTRIBUTION'} = $product->{'installconfig'}->{'distribution'};
317     $info{'DESCRDIR'}     = $product->{'installconfig'}->{'descriptiondir'};
318     $info{'DATADIR'}      = $product->{'installconfig'}->{'datadir'};
319     if (defined($obsname) && (defined($prodRef->{'project'}->{'name'})) && ("$prodRef->{'project'}->{'name'}" ne "")){
320         $info{'REPOID'}       = "obsproduct:://".$obsname."/".$prodRef->{'project'}->{'name'}."/".$product->{'name'}."/".$product->{'version'}."/".$medium->{'flavor'}."/".join( "-", @allarchs );
321     }
322     if (defined($medium->{'preselected_patterns'})){
323         $info{'PATTERNS'} .= join(' ',map( $_->{'name'},@{$medium->{'preselected_patterns'}->[0]->{'pattern'}}));
324     }
325     foreach my $summary ( @{$product->{'summary'}} ){
326       $info{'LABEL'}      = $summary->{'_content'} if ( ! $summary->{'language'} );
327     }
328     foreach my $shortsummary ( @{$product->{'shortsummary'}} ){
329       $info{'SHORTLABEL'}      = $shortsummary->{'_content'} if ( ! $shortsummary->{'language'} );
330     }
331     $options{'REPO_LOCATION'} = getUrl($product,$arch,"repository");
332     $info{'RELNOTESURL'} = getUrl($product,$arch,"releasenotes");
333
334     $info{'LINGUAS'} = "";
335     foreach my $language ( @{$product->{'linguas'}->{'language'}} ){
336       $info{'LINGUAS'} .= "$language->{'_content'} ";
337     }
338
339     $info{'BASEARCHS'} = "";
340     foreach my $ar ( @$archSetList ) {
341         $info{'BASEARCHS'} .= "$archSets{$ar->{ref}}->{productarch} " if( $archSets{$ar->{ref}} );
342     }
343
344     # Add REGISTERPRODUCT string - see: bnc#458340
345     if (defined($medium->{'registration'}) and $medium->{'registration'} eq "false") {
346         $info{'REGISTERPRODUCT'} .= "false"
347     } elsif (defined($medium->{'productdependency'})) {
348         $info{'REGISTERPRODUCT'} .= "true" if (defined($product->{'register'}));
349     }
350
351     # Add Product Options
352     my @vars1;
353     foreach my $opt ( sort keys %options ) {
354         push @vars1, { name => $opt, _content => $options{$opt} };
355     }
356     $re->{productoption} = \@vars1;
357
358     # Add Product Info
359     my @info;
360     push @info, { name => 'CONTENTSTYLE', _content => '11' }; # Needs to be first !
361     foreach my $opt ( sort keys %info) {
362         push @info, { name => $opt, _content => $info{$opt} };
363     }
364     $re->{productinfo} = \@info;
365
366     return $re;
367 }
368
369 sub createMetadata( $$$ )
370 {
371     my( $prodRef, $medium, $archSetList ) = @_;
372     
373     return undef unless( $medium->{'metadata'} );
374     my $re = {};
375
376     # print "> " . Dumper $medium->{metadata};
377
378     my @packages;
379     my @files;
380     my $metadata_medium = "0";
381 #    $metadata_medium = "1" if (defined($medium->{'sourcemedia'}) && $medium->{'sourcemedia'} > 1);
382     foreach my $pack ( @{ $medium->{metadata}->{package} } ) {
383       my @onlyarch;
384       my $removearch;
385       if ($pack->{removearch}){
386         next if containsMyArch( $prodRef, $archSetList, $pack->{removearch} );
387         $removearch = "$pack->{removearch},src,nosrc";
388       } else {
389         $removearch = "src,nosrc";
390       }
391       if (defined($pack->{onlyarch})) {
392         push @onlyarch, $pack->{onlyarch};
393       } else {
394         foreach my $requiredArch( @{$archSetList} ) {
395           my $ref = $requiredArch->{ref};
396           die( "ERROR: No such archset $requiredArch\n" ) unless $archSets{$ref};
397           push @onlyarch, $archSets{$ref}->{productarch} unless grep { $_ eq $archSets{$ref}->{productarch} } @onlyarch;
398         }
399         # In 11.4 and later noarch metapackages need explicit a onlyarch="noarch" in config because we do now fail on missing packages
400         if ((not defined $medium->{'mediastyle'}) || $medium->{'mediastyle'} eq "suse-11.1" || $medium->{'mediastyle'} eq "suse-11.2" || $medium->{'mediastyle'} eq "suse-11.3") {
401           push @onlyarch, "noarch" unless grep { $_ eq "noarch" } @onlyarch;
402         }
403       }
404       my $h = { name => $pack->{name},
405                 medium => $metadata_medium,
406                 removearch => $removearch,
407                 onlyarch => join(",",@onlyarch) };
408       $h->{'arch'} = $pack->{'arch'} if defined($pack->{'arch'});
409       $h->{'addarch'} = $pack->{addarch} if defined($pack->{addarch});
410       push @packages, $h;
411     }
412
413     my @a;
414     return { repopackage => \@packages };
415     
416 #     my @files;
417 #     foreach my $file ( @{ $medium->{metadata}->{file} } ) {
418 #       push @files, { name => $file->{name} };
419 #     }
420 #     # push @a, { file => \@files }; CHECK: Needed?
421
422 #     return \@a;
423 }
424
425 sub containsMyArch( $$$ )
426 {
427     my ($prodRef, $archSetList, $archList ) = @_;
428
429     foreach my $s( split( /\s*,\s*/, $archList ) ){
430       foreach my $requiredArch( @{$archSetList} ) {
431         my $ref = $requiredArch->{ref};
432         die( "ERROR: No such archset $requiredArch\n" ) unless $archSets{$ref};
433         return 1 if ( $s eq $archSets{$ref}->{productarch} );
434       }
435     }
436     return 0;
437 }
438
439 sub useToPackages( $$$ )
440 {
441     my ($prodRef, $medium, $archSetList ) = @_;
442
443     return unless $medium;
444
445     my @packages;
446
447     if (defined($medium->{use_undecided}) && $medium->{use_undecided} eq "true" ) {
448        # Simply take all packages ?
449        push @packages, { name => "*" };
450     };
451
452     return unless $medium->{use};
453     my @useStatements = @{$medium->{use} };
454
455     # print "Use Required: <$useRequired>, Suggested: <$useSuggested>, Recommended: <$useRecommended>\n";
456
457     foreach my $useState ( @useStatements ) {
458         my $useRequired;
459         my $useRecommended;
460         my $useSuggested;
461
462         # Media default settings
463         $useRequired    = $medium->{'use_required'}
464                           if ( defined($medium->{'use_required'}) && $medium->{'use_required'} eq "true" );
465         $useRecommended = $medium->{'use_recommended'}
466                           if ( defined($medium->{'use_recommended'}) && $medium->{'use_recommended'} eq "true" );
467         $useSuggested   = $medium->{'use_suggested'}
468                           if ( defined($medium->{'use_suggested'}) && $medium->{'use_suggested'} eq "true" );
469
470         # can get overriden by "use group" settings
471         $useRequired    = $useState->{'use_required'}
472                           if ( defined($useState->{'use_required'}) && $useState->{'use_required'} eq "true" );
473         $useRecommended = $useState->{'use_recommended'}
474                           if ( defined($useState->{'use_recommended'}) && $useState->{'use_recommended'} eq "true" );
475         $useSuggested   = $useState->{'use_suggested'}
476                           if ( defined($useState->{'use_suggested'}) && $useState->{'use_suggested'} eq "true" );
477     
478         if( $useState->{group} ) {
479 #           print "Handling use of group $useState->{group}\n";
480             push @packages, groupToPackages( $prodRef, $archSetList,
481                                              $useState->{group}, 
482                                              $useRequired, 
483                                              $useRecommended,
484                                              $useSuggested );
485             # there might be additional packages listed in the group.
486             if( $useState->{package} ) {
487                 foreach my $addPack ( @{$useState->{package} } ) {
488                     # print Dumper( $addPack ) . "\n";
489                     my $relType = $addPack->{relationship};
490                     die( "ERROR: Unknown relation type string for package add!\n" ) unless( $relType eq "requires" || $relType eq "recommends" || $relType eq "suggests" );
491                     if( ( $useRequired    && $addPack->{relationship} eq "requires") ||
492                         ( $useRecommended && $addPack->{relationship} eq "recommends" ) ||
493                         ( $useSuggested   && $addPack->{relationship} eq "suggests" ) ) {
494
495                         my %tmp;
496                         $tmp{name} = $addPack->{name};
497                         $tmp{medium} = $addPack->{medium} if (defined($addPack->{medium}));
498                         if ($addPack->{removearch}) {
499                           next if containsMyArch( $prodRef, $archSetList, $addPack->{removearch} );
500                           $tmp{removearch} = $addPack->{removearch};
501                         }
502                         push @packages, \%tmp;
503                     }
504                 }
505             }
506         } elsif( $useState->{pattern} ) {
507             die( "ERROR: Patterns are not supported for repopackages!\n" );
508         }
509     }
510     return \@packages;
511 }
512
513 sub groupToPackages( $$$$$ ) 
514 {
515     my ($prodRef, $archSetList, $group, $useReq, $useRec, $useSug ) = @_;
516
517     # generate the list of current architectures out of the archSetList
518     # FIXME: In all product configs I saw so far, there is only one entry 
519     # in the archsetlist.
520     # What does it mean if there are more? The following code takes all
521     # and allows all.
522     my @validArchs;
523     foreach my $archHashRef (@$archSetList) {
524         my $archSetRef = $archSets{$archHashRef->{ref}};
525         push @validArchs, $archSetRef->{productarch};
526     }
527
528     my @groups = @{$prodRef->{group} || {}}; 
529     my $groupRef;
530
531     # search for the group we should convert here.
532     foreach my $gl( @groups ) {
533         if( $gl->{name} eq $group ) {
534             $groupRef = $gl;
535             last;
536         }
537     }
538
539     unless( $groupRef ) {
540         die( "ERROR: Group <$group> not found!\n" );
541     }
542     
543     unless( $groupRef->{packagelist} ) {
544         die( "ERROR: Group <$group> has no package lists!\n" );
545     }
546
547     # ok, here we have a valid group reference.
548 #    print " * resolving group <$groupRef->{name}>\n";
549     my @packagelists = @{$groupRef->{packagelist}};
550
551     my %conditionTemplate;
552     foreach my $condList( @{$groupRef->{conditional} } ) {
553 #       print "Handling group conditional $condList->{name}\n";
554         my $cond = $conditionals{ $condList->{name} };
555
556         if( $cond->{platform} ) {
557             my @platforms = @{$cond->{platform}};
558
559             # the condition only becomes a template
560             foreach my $p ( @platforms ) {
561                 my @condArchs;
562                 @condArchs  = split( /\s*,\s*/, $p->{arch} ) if( $p->{arch} );
563                 # 
564                 my $takeIt = 1; # Take all condition tags if no arch-tag is there
565                 if( $p->{arch} ) {
566                     $takeIt = 0;
567                     foreach my $validArch( @validArchs ) {
568                         if( grep( /$validArch/, @condArchs ) ) {
569                             $takeIt = 1;
570                             last;
571                         }
572                     }
573                 }
574                 if( $takeIt ) {
575                     %conditionTemplate = (%conditionTemplate, %{$p});
576                 } else {
577                     # This condition does not match, so drop it
578                 }
579             }
580         }
581     }
582
583     # Drop this group, if condition(s) exist for it, but none matches for this platform
584     return () if ( @{$groupRef->{conditional}} > 0 && !keys %conditionTemplate );
585  
586     my $useFlags = { requires => $useReq || 0, recommends => $useRec || 0, suggests => $useSug || 0 };
587
588     my @resultList;
589
590     foreach my $packList ( @packagelists ) {
591         my $relation = $packList->{relationship} || 'requires';
592         # print "Relation: $relation\n";
593         if( $useFlags->{$relation} && $packList->{package} ) {
594             # parse the package in 
595             my @packs = @{$packList->{package}};
596             foreach my $pack ( @packs ) {
597                 my %h = %conditionTemplate;
598                 my $takeIt = 1;
599
600                 $takeIt = 0 unless $pack->{conditional};
601
602                 # print Dumper $pack;
603                 foreach my $condList( @{$pack->{conditional} } ) {
604                     my $name = $condList->{name};
605                     my $cond = $conditionals{$name};
606                     next unless defined $h{$name};
607                     $takeIt = 1;
608                     print "Handling package conditional $name\n";
609                     # print Dumper "Conditional: ". $cond . "\n";
610                     
611                     if( $cond->{platform} ) {
612                         my @platforms = @{$cond->{platform}};
613                         foreach my $p ( @platforms ) {
614                             %h= (%h, %{$p});
615                         }
616                     }
617                     if( $cond->{media} ) {
618                         $h{medium} = $cond->{media}->{number};
619                     }
620                 }
621                 $h{name} = $pack->{name};
622                 push @resultList, \%h;
623             }
624         }
625
626     }
627
628     return @resultList;
629 }
630
631 #
632 # This sub expands the patterns 
633 sub expandPackages( $ )
634 {
635     my ($groupRef) = @_;
636     
637     my $name = $groupRef->{name};
638
639     print "Working on group $name\n";
640     
641     my @patterns = @{$groupRef->{pattern}};
642     
643     my $pat = @{$groupRef->{pattern}}[0];
644     $groupRef->{_pattern} = $pat;
645
646
647     foreach my $pack ( @{$groupRef->{group}} ) {
648         my $packListRef = $pack->{package};
649         my $relation = $pack->{relationship};
650         my @resultPacks;
651         foreach my $packRef ( @${packListRef} ) {
652             # print "Pushing $packRef->{name}\n";
653             my %packValues;
654             $packValues{name} = $packRef->{name};
655             if( $groupRef->{platform} ) {
656                 # forcerepo??
657                 foreach my $tag ('forcearch', 'addarch', 'onlyarch', 'removearch', 'source', 'script', 'medium' ) {
658                     $packValues{$tag} = $groupRef->{platform}->{$tag} if( $groupRef->{platform}->{$tag} );
659                 }
660             }
661
662             push @resultPacks, \%packValues;
663         }
664         my $keyname = "_" . lc $relation;
665         print "Keyname of package list: $keyname\n";
666         $groupRef->{$keyname} = \@resultPacks;
667     }
668 }
669
670 #
671 # Creation of the instsource part of the kiwi file
672 #
673 # note that the product spec contains a list of archsets. For each of these archsets and 
674 # for each of the media must be a separate kiwi file.
675 #
676 # 1. parameter: the reference on the product datastructure
677 # 2. parameter: the reference on the current media datastructure
678 # 3. parameter: list of the archs for this kiwi file.
679 #
680 sub createInstsource( $$$ )
681 {
682     my( $prodRef, $medium, $archSetList ) = @_;
683     my $re = {};
684
685     $re->{architectures} = createArchitectures( $archSetList );
686     $re->{productoptions} = createProductOptions( $prodRef, $medium, $archSetList );
687     
688     my @r;
689     my $count = 0;
690     foreach my $repo ( @{$prodRef->{repositories}{repository} } ) {
691         my %h;
692         my $kiwipath;
693         next if defined($repo->{build}) && $repo->{build} eq "ignore";
694         $count = $count + 1;
695         $h{priority} = $count;
696         $h{name} = "repository_".$count;
697         if ($repo->{path} =~ /^obs:\/\/([^\/]*)\/([^\/]*)$/ ) { #old format without obsname 
698           $h{local} = "true";
699           $kiwipath = "$1/$2";
700         } elsif ($repo->{path} =~ /^obs:\/\/([^\/]*)\/([^\/]*)\/([^\/]*)$/ ) {
701           $h{local} = "true";
702           $kiwipath = "$2/$3";
703         } else {
704           die( "ERROR: Non obs:// url as repository: $repo->{path} !\n" );
705         };
706         if ( $runOnServer ) {
707           for my $arch ( @{$re->{architectures}->{requiredarch}} ) {
708              my $path = "$bsdir/build/$kiwipath/$arch->{'ref'}/:full";
709              $h{source} = { path => $path };
710              push @r, \%h;
711              print "WARNING: local path $path does not exist !\n" if ( ! -e "$path" );
712           };
713         }else{
714           $h{source} = { path => "obs://".$kiwipath };
715           push @r, \%h;
716         };
717     }
718     $re->{instrepo} = \@r;
719     
720     # metadata, media dependant
721     my $ref = createMetadata( $prodRef, $medium, $archSetList );
722     if( $ref ) {
723         $re->{metadata} = createMetadata( $prodRef, $medium, $archSetList );
724     }
725
726     # repopackages
727     my @packages;
728     my $useToPacks = useToPackages( $prodRef, $medium, $archSetList );
729
730     if( $useToPacks ) {
731         push @packages, { repopackage => $useToPacks };
732     }
733     # print "Packlist: " . Dumper \@packages;
734     $re->{repopackages} = \@packages;
735
736     return $re;
737 }
738
739 sub createRepository
740 {
741     # This is for a dummy entry, it is required by the kiwi DTD, but not used
742     # for installation medias.
743     my( $prodRef ) = @_;
744     my @repository;
745     my $source;
746     my $dummydir = "/var/lib/empty";
747
748     # Do we have ever a different repo type than "yast" on products ?
749     $source->{ 'path' } = $dummydir;
750     push @repository, { 'type' => 'yast2', 'source' => $source };
751
752     return \@repository;
753 }
754
755 sub createFlavorReadme( $$ ){
756     my($prodRef,$product)=@_;
757     my $reame_file="";
758     foreach my $flavor ( @{$prodRef->{mediasets}->{media}} ){
759       next if ((!defined ($flavor->{'flavor'}) || ("$flavor->{'flavor'}" eq "")));
760       my $readmedir = "\$RPM_BUILD_ROOT/%{_defaultdocdir}/$product->{name}-release-$flavor->{flavor}";
761       $reame_file .= "mkdir -p $readmedir\n";
762       $reame_file .= "cat >$readmedir/README << EOF\n";
763       $reame_file .= "This package just exist just for providing the product flavor \'$flavor->{flavor}\'.\n";
764       $reame_file .= "\nEOF\n\n";
765     }
766     return $reame_file;
767 }
768
769 sub writeMigrationSPECfile( $$$$$ )
770 {
771     my( $file, $prodRef, $product, $newpatchlevel, $migtarget ) = @_;
772     my $mproduct = Storable::dclone($product);
773
774     if ($migtarget) {
775         $mproduct->{name} = $product->{name}."-".$migtarget."-migration";
776     } else {
777         $mproduct->{name} = $product->{name}."-SP".$newpatchlevel."-migration";
778     }
779
780     # Upgrade section must not be in migration to avoid double notification
781     delete $mproduct->{upgrades};
782
783     # Set default values for release package reference
784     $mproduct->{'installconfig'}->{'releasepackage'} = { 'flag' => "EQ" };
785     $mproduct->{'installconfig'}->{'releasepackage'}->{'name'} = '%{name}';
786     $mproduct->{'installconfig'}->{'releasepackage'}->{'version'} = '%{version}';
787     $mproduct->{'installconfig'}->{'releasepackage'}->{'release'} = '%{release}';
788
789     my $content="# ";
790     $content.="\n\n";
791
792     my $package_name = $mproduct->{name};
793     $package_name =~ s/\./_/g;
794     $content.="Name:           $package_name\n";
795
796     if ($migtarget) {
797         $content.="Summary:        $product->{name} $migtarget Migration Product\n";
798     } else {
799         $content.="Summary:        $product->{name} Service Pack $newpatchlevel Migration Product\n";
800     }
801     $content.="Version:        ".$product->{version}."\n";
802     $content.="Release:        0\n";
803     $content.="License:        BSD 3-Clause\n";
804     $content.="Group:          System/Fhs\n";
805     $content.="Provides:       product()\n";
806     $content.="Provides:       product(%name) = %{version}-%{release}\n";
807     $content.="Requires:       product(".$product->{name}.") = ".$product->{version}."\n";
808     $content.="AutoReqProv:    on\n";
809     $content.="BuildRoot:      %{_tmppath}/%{name}-%{version}-build\n";
810     $content.="\n%description\n";
811     if ($migtarget) {
812         $content.="Product to migrate to $product->{name} $migtarget.\n";
813     } else {
814         $content.="Product to migrate to $product->{name} Service Pack $newpatchlevel.\n";
815     }
816     $content.="\n\n";
817     $content.="\n%prep\n";
818     $content.="\n%build\n\n";
819     $content.="\n%install\n";
820     $content.=createProductFile($prodRef,$mproduct);
821     $content.="\n%clean\n";
822     $content.="rm -rf %buildroot\n";
823     $content.="\n%files\n";
824     $content.="%defattr(644,root,root,755)\n";
825     $content.="%dir /etc/products.d\n";
826     $content.="/etc/products.d/*.prod\n";
827     $content.="\n%changelog\n";
828
829     # write out the modified file.
830     writestr($file, undef, $content);      
831 }
832
833 sub writeProductSPECfile( $$$$ )
834 {
835     my( $file, $infile, $prodRef, $product ) = @_;
836     my $product_flavors="";
837
838     # take media style from first media. not nice, but we can only have one product package for all of them.
839     my $medium = $prodRef->{mediasets}->{media}[0];
840
841     ### A product may obsolete packages.
842     my $obsoletepackage="";
843     for my $p ( @{$product->{'installconfig'}->{'obsoletepackage'}} ) {
844       $obsoletepackage .= "\nProvides: weakremover(".$p->{'_content'}.")";
845     }
846     $obsoletepackage .= "\n";
847     # My product provides
848     my $productprovides="";
849     $productprovides.="Provides:       %name-%version\n";
850     $productprovides.="Provides:       $prodRef->{'project'}->{'name'}\n" if ((defined($prodRef->{'project'}->{'name'})) && ("$prodRef->{'project'}->{'name'}" ne ""));
851     $productprovides.="Provides:       product()\n";
852     if ((not defined $medium->{'mediastyle'}) || $medium->{'mediastyle'} eq "suse-11.1" || $medium->{'mediastyle'} eq "suse-11.2" || $medium->{'mediastyle'} eq "suse-11.3") {
853       $productprovides.="Provides:       product($product->{'name'}) = %version-%release\n";
854     } else {
855       $productprovides.="Provides:       product($product->{'name'}) = $product->{version}-$product->{release}\n";
856     }
857     if (defined($product->{'patchlevel'}) && $product->{'patchlevel'} ne '0') {
858       $productprovides.="Provides:       product($product->{'name'}-SP$product->{'patchlevel'}) = %version-%release\n";
859       $productprovides.="Obsoletes:      ".$product->{name}."-SP".$product->{'patchlevel'}."-migration\n";  # release package name
860       $productprovides.="Obsoletes:      product:".$product->{name}."-SP".$product->{'patchlevel'}."-migration\n"; # product name
861     }
862     $productprovides.="Requires:       product_flavor($product->{name})\n";
863
864     if ("$infile" eq ""){
865      my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydat,$isdst)=localtime();
866      $year += 1900;
867      my $content=();
868      # write the specfile header
869      $content="#
870 # spec file for package $product->{name}-release (Version $product->{version})
871 #
872 # Copyright (c) $year $product->{'vendor'}.
873 #
874 # All modifications and additions to the file contributed by third parties
875 # remain the property of their copyright owners, unless otherwise agreed
876 # upon. The license for this file, and modifications and additions to the
877 # file, is the same license as for the pristine package itself (unless the
878 # license for the pristine package is not an Open Source License, in which
879 # case the license is the MIT License). An \"Open Source License\" is a
880 # license that conforms to the Open Source Definition (Version 1.9)
881 # published by the Open Source Initiative.
882 ";
883      my $bugtracker=getUrl($product,"i586","bugtracker");
884      $content.="\n# Please submit bugfixes or comments via $bugtracker\n#\n\n" if ("$bugtracker" ne "");
885      $content.="\n\n";
886      $content.="Name:           $product->{name}-release\n";
887      $content.="%define         product $product->{name}\n";
888      if (defined ($product->{'buildconfig'}->{'betaversion'})){
889        $content.="%define         betaversion $product->{'buildconfig'}->{'betaversion'}\n";
890      }
891      foreach my $summary ( @{$product->{'summary'}} ){
892        $content.="Summary:        $summary->{_content}\n" if ( ! $summary->{'language'} );
893      }
894      $content.="Version:        ".$product->{version}."\n";
895      $content.="Release:        0\n"; # FIXME: check if this is this really handled via BS
896      $content.="License:        BSD 3-Clause\n";
897      $content.="Group:          System/Fhs\n";
898      $content.=$obsoletepackage;
899      $content.=$productprovides;
900      $content.="\nAutoReqProv:    on\n";
901      $content.="BuildRoot:      %{_tmppath}/%{name}-%{version}-build\n";
902      $content.="\n%description\n";
903      for my $description ( @{$product->{'description'} || []} ){
904        $content.="$description->{_content}\n" if ( ! $description->{'description'} );
905      }
906      $content.="\n\n";
907      $content.=createSPECfileFlavors($prodRef,$product);
908      $content.="\n%prep\n";
909      $content.="\n%build\n\n";
910      $content.="\n%install\n";
911      $content.=createFlavorReadme($prodRef,$product);
912      $content.=createProductFile($prodRef,$product);
913      $content.="\n%clean\n";
914      $content.="rm -rf %buildroot\n";
915      $content.="\n%files\n";
916      $content.="%defattr(644,root,root,755)\n";
917      $content.="%dir /etc/products.d\n";
918      $content.="/etc/products.d/*.prod\n";
919      $content.="\n%changelog\n";
920
921      # write out the modified file.
922      writestr($file, undef, $content);      
923  
924     } else {
925
926      $product_flavors.=createSPECfileFlavors($prodRef,$product);
927
928      # Create product file to be packaged
929      my $zypp_product_file = createProductFile($prodRef,$product);
930         $zypp_product_file.= createFlavorReadme($prodRef,$product);
931
932      my $str = readstr($infile);
933
934      # replace all strings
935      $str =~ s/___DISTNAME___/$product->{name}/g;
936      $str =~ s/___BASE_VERSION___/$product->{baseversion}/g;
937      $str =~ s/___VERSION___/$product->{version}/g;
938      if ( defined $product->{buildconfig}->{betaversion} ) {
939        $str =~ s/___BETA_VERSION___/$product->{buildconfig}->{betaversion}/g;
940      } else {
941        $str =~ s/___BETA_VERSION___//g;
942      }
943      if ((not defined $medium->{'mediastyle'}) || $medium->{'mediastyle'} eq "suse-11.1" || $medium->{'mediastyle'} eq "suse-11.2" || $medium->{'mediastyle'} eq "suse-11.3") {
944         # this is the product release, not a package release. should not be used anymore.
945         # this old way is a problem because it can produce leading zeros in product provides
946         $str =~ s/___RELEASE___/0%{?release}/g;
947      } else {
948         # product release is not really used so far, but defined in zypp stack. So let's
949         # support it. But it is so far 0 on all our products
950         $str =~ s/___RELEASE___/$product->{release}/g;
951      }
952      $str =~ s/___PATCH_LEVEL___/$product->{'patchlevel'}/g;
953      $str =~ s/___PACKAGE_NAME___/$product->{name}-release/g;
954      $str =~ s/___PRODUCT_NAME___/$product->{name}/g;
955      $str =~ s/___SUMMARY___/$product->{summary}[0]->{_content}/g; # FIXME: find the non-lang one
956      $str =~ s/___DESCRIPTION___/$product->{description}[0]->{_content}/g; # FIXME: find the non-lang one
957      $str =~ s/___FLAVOR_PACKAGES___/$product_flavors/g;
958      $str =~ s/___CREATE_PRODUCT_FILES___/$zypp_product_file/g;
959
960      $str =~ s/___PRODUCT_PROVIDES___/$productprovides/g;
961      $str =~ s/___OBSOLETE_PACKAGES___/$obsoletepackage/g;
962  
963      # write out the modified file.
964      writestr($file, undef, $str);
965     }
966 }
967
968 sub createSPECfileFlavors ( $$ ) {
969     my ( $prodRef,$product ) = @_;
970     my $product_flavors="";
971     foreach my $flavor ( @{$prodRef->{mediasets}->{media}} ){
972       next if ((!defined ($flavor->{'flavor'}) || ("$flavor->{'flavor'}" eq "")));
973       $product_flavors.="%package $flavor->{flavor}\n";
974       $product_flavors.="License:        BSD 3-Clause\n";
975       $product_flavors.="Group:          System/Fhs\n";
976       if ((defined($prodRef->{'project'}->{'name'})) && ("$prodRef->{'project'}->{'name'}" ne "")){
977           # TODO: - split between ":" -> Provides: SUSE \n Provides: SUSE:Factory ...
978           #       - add plattform
979           $product_flavors.="Provides:       $prodRef->{'project'}->{'name'}\n";
980       }
981       $product_flavors.="Provides:       product_flavor()\n";
982       $product_flavors.="Provides:       flavor($flavor->{flavor})\n";
983       # take media style from first media. not nice, but we can only have one product package for all of them.
984       my $medium = $prodRef->{mediasets}->{media}[0];
985       if ((not defined $medium->{'mediastyle'}) || $medium->{'mediastyle'} eq "suse-11.1" || $medium->{'mediastyle'} eq "suse-11.2" || $medium->{'mediastyle'} eq "suse-11.3") {
986         $product_flavors.="Provides:       product_flavor($product->{name}) = %version-%release\n";
987       } else {
988         # this is the product version and release, not a package release.
989         $product_flavors.="Provides:       product_flavor($product->{name}) = $product->{version}-$product->{release}\n";
990       }
991       if (defined($flavor->{'productdependency'})){
992         foreach my $dependency (@{$flavor->{'productdependency'}}){
993             my ($relship, $version, $patchlevel, $flavor, $release, $flag);
994             $relship = convertRelationship($dependency->{'relationship'});
995             $version = $dependency->{'version'} if defined($dependency->{'version'});
996             $patchlevel = "-SP".$dependency->{'patchlevel'} if defined($dependency->{'patchlevel'});
997             $flavor = "-".$dependency->{'flavor'} if defined($dependency->{'flavor'});
998             $release = $dependency->{'release'} if defined($dependency->{'release'});
999             $flag = convertFlags($dependency->{'flag'}) if defined($dependency->{'flag'});
1000             if (!$flag && ($version ne "")) { # avoid something like Requires: sles 11
1001                 $flag = "=";
1002             }
1003             if ((defined($version)) && ($version ne "") && (defined($release)) && ($release ne "")){ # rpm style for version-release
1004                 $release = "-".$release;
1005             } 
1006             $patchlevel = "" unless defined($patchlevel);
1007             $release = "" unless defined($release);
1008             $flavor = "" unless defined($flavor);
1009             
1010             $product_flavors.="$relship:       product(".$dependency->{'name'}."$patchlevel$flavor) $flag $version$release\n";
1011         }
1012       }
1013       foreach my $summary ( @{$product->{'summary'}} ){
1014         $product_flavors.="Summary:        $summary->{_content}\n" if ( ! $summary->{'language'} );
1015       }
1016       $product_flavors.="\n";
1017       $product_flavors.="%description $flavor->{flavor}\n";
1018       foreach my $description ( @{$product->{'description'}} ){
1019         $product_flavors.="$description->{_content}\n" if ( ! $description->{'description'} );
1020       }
1021       $product_flavors.="\n";
1022       $product_flavors.="%files $flavor->{flavor}\n";
1023       $product_flavors.="%defattr(-,root,root)\n";
1024       $product_flavors.="%doc %{_defaultdocdir}/$product->{name}-release-$flavor->{flavor}\n";
1025       $product_flavors.="\n"
1026     }
1027     return $product_flavors;
1028 }
1029
1030 sub createSPECfileInstallSection ( $ ) {
1031         my ($product) = @_;
1032         my $content="";
1033         my $is_main_product=0;
1034         if ( $is_main_product ){
1035                 my $greeting = $product->{'name'}." ".$product->{'version'};
1036                 foreach my $summary ( @{$product->{'summary'}} ){
1037                    $greeting = $summary->{'_content'} if ( ! $summary->{'language'} );
1038                 }
1039
1040                 my $content="mkdir -p %{buildroot}/%{_sysconfdir}
1041 echo -e 'Welcome to ".$greeting." %{?betaversion:%{betaversion} }- Kernel \\r (\\l).\n\n' > %{buildroot}/etc/issue
1042 echo \"Welcome to ".$greeting." %{?betaversion:%{betaversion} }- Kernel %%r (%%t).\" > %{buildroot}/etc/issue.net
1043 echo \"#".$greeting." %{?betaversion:%{betaversion} }(%{_target_cpu})\" > %{buildroot}/etc/SuSE-release
1044 echo \"VERSION = %{version}\" >> %{buildroot}/etc/SuSE-release\n";
1045
1046                 $content.="PATCHLEVEL = ".$product->{'patchlevel'}."\n" if (defined($product->{'patchlevel'}));
1047                 $content.="mkdir -p %{buildroot}/%{_sysconfdir}
1048 echo \"Have a lot of fun...\" > %{buildroot}/etc/motd
1049 # Bug 404141 - /etc/YaST/control.xml should be owned by some package
1050 mkdir -p %{buildroot}/etc/YaST2/
1051 install -m 644 /CD1/control.xml %{buildroot}/etc/YaST2/
1052 install -m 644 -D /CD1/EULA.txt %{buildroot}/%{_docdir}/%{name}/%{product}-EULA.txt
1053 ";
1054         }
1055         $content="mkdir -p %{buildroot}/etc/products.d";
1056         return $content;
1057 }
1058
1059 sub createProductFile ( $$ ) {
1060     my ($prodRef, $product) = @_;
1061     my $zypp_product_file = "";
1062     my $zypp_product = Storable::dclone($product);
1063     my $d;
1064     my $pfile = "\$RPM_BUILD_ROOT/etc/products.d/$product->{name}.prod";
1065
1066     $zypp_product_file = "mkdir -p \$RPM_BUILD_ROOT/etc/products.d\n";
1067     $zypp_product->{'arch'} = '%{_target_cpu}'; # write product architecture during rpm build
1068     $zypp_product->{'schemeversion'} = "0";
1069
1070     $d->{"target"}  = $product->{'register'}->{'target'};
1071     $d->{"release"} = $product->{'register'}->{'release'};
1072     my @r;
1073     foreach my $repo ( @{$prodRef->{repositories}{repository} } ) {
1074         next if defined($repo->{product_file}) && $repo->{product_file} eq "ignore";
1075         # do only export when build name is specified
1076         push @r, { "path" => $repo->{path} } if $repo->{path} =~ /^obs:\/\/(.*)\/(.*)\/(.*)/ && $1 && $2 && $3 ;
1077     };
1078     $d->{"repositories"}{"repository"} = \@r;
1079     $zypp_product->{'register'} = $d;
1080
1081     # Release package number shall go with the package release
1082     $zypp_product->{'release'} = $product->{release};
1083
1084     my $mediaStyle = "suse-11.1"; # fallback value
1085     my $medium = $prodRef->{mediasets}->{media}[0];
1086     $mediaStyle = $medium->{'mediastyle'} if (defined($medium->{'mediastyle'}));
1087     if ($mediaStyle =~ /^suse-11.[123]$/) {
1088       $zypp_product->{'release'} = "%{release}";
1089     }
1090
1091     my $xml = XMLout( $BSProductXML::product, $zypp_product );
1092     die ( "ERROR: Unable to create xml for $product->{name} !" ) unless $xml;
1093     $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n$xml";
1094     $zypp_product_file .= "cat >$pfile << EOF\n";
1095     $zypp_product_file .= "$xml\nEOF\n\n";
1096 }
1097
1098 # Process the commandline arguments.
1099 getopts('dlhm:');
1100
1101 usage() if $opt_h;
1102 $runOnServer = "true" if $opt_l;
1103
1104 my ($infile, $outdir, $project) = @ARGV;
1105
1106 die( "Please specify input file, output directory (and project name)\n" ) unless $infile;
1107 die( "Please specify output directory (and project) name\n" ) unless $outdir;
1108
1109 my $d;
1110 # global indir
1111 ($d, $indir) = fileparse( $infile );
1112
1113 my $prodRef = readProductFile( $infile );
1114
1115 #
1116 # Sanity checks
1117 #
1118 die("product definition contains no products\n") unless $prodRef->{'products'};
1119 die("product definition contains multiple products, this is not yet supported \n") if @{$prodRef->{'products'}->{'product'}} > 1;
1120 my $product = $prodRef->{'products'}->{'product'}[0];
1121 die("no product name set\n") unless $product->{'name'};
1122 die("illegal product name: $product->{'name'}\n") if $product->{'name'} =~ /^[_\.]/;
1123 die("illegal product name: $product->{'name'}\n") if $product->{'name'} =~ /[\/\000-\037]/;
1124 die("ERROR: File name does not match to product name ($infile/$prodRef->{'products'}->{'product'}[0]->{'name'}.product)\n") if not $infile =~ /.*\/$prodRef->{'products'}->{'product'}[0]->{'name'}.product$/;
1125
1126 #
1127 # Calculate version strings
1128 #
1129 die ("It is not allowed to specify baseversion and version.") if (defined($product->{'baseversion'}) && defined($product->{'version'}) );
1130 if (defined($product->{'baseversion'})) {
1131     $product->{'version'}      = $product->{'baseversion'};
1132     $product->{'version'}     .= ".".$product->{'patchlevel'} if (defined($product->{'patchlevel'}) && $product->{'patchlevel'} ne '0');
1133 };
1134
1135 #
1136 # Check missing information of release package requirement and set defaults if neeeded.
1137 #
1138 $product->{'installconfig'}->{'releasepackage'} = { 'flag' => "EQ" }
1139                 if ( not defined($product->{'installconfig'}->{'releasepackage'}) );
1140 $product->{'installconfig'}->{'releasepackage'}->{'flag'} = 'EQ'
1141                 if ( not defined($product->{'installconfig'}->{'releasepackage'}->{'flag'}) );
1142 $product->{'installconfig'}->{'releasepackage'}->{'name'} = '%{name}'
1143                 if ( not defined($product->{'installconfig'}->{'releasepackage'}->{'name'}) );
1144 $product->{'installconfig'}->{'releasepackage'}->{'version'} = '%{version}'
1145                 if ( not defined($product->{'installconfig'}->{'releasepackage'}->{'version'}) );
1146 $product->{'installconfig'}->{'releasepackage'}->{'release'} = '%{release}'
1147                 if ( not defined($product->{'installconfig'}->{'releasepackage'}->{'release'}) );
1148
1149 #
1150 # Create a kiwi configuration for each distribution flavor
1151 #
1152
1153 my $productRef = $prodRef->{products}->{product}->[0]; # FIXME: Support multiple products.
1154
1155 my $kiwiImage = {};
1156 my $name = sprintf( "OBS__%s___%s",
1157                     $product->{name},
1158                     $product->{version} );
1159
1160 $kiwiImage->{name} = $name;
1161
1162 $kiwiImage->{description} = createDescription( $productRef );
1163 # so far for all media types identical. Now loop over the media types
1164 # to create media type specific versions;
1165
1166 parseConditionals( $prodRef->{conditionals}->{conditional} );
1167 parseArchsets( $prodRef->{archsets}{archset} );
1168
1169 #
1170 # Create $product-release packages
1171 #
1172
1173 # handle migration case
1174 if (defined($product->{migrationtarget})){
1175   my $newpatchlevel = 0;
1176   my $productname = $product->{name}."-".$product->{migrationtarget}."-migration";
1177   my $packagename = $productname;
1178   $packagename =~ s/\./_/g;
1179
1180   # enforce correct upgrade product name
1181   for my $upgrade ( @{$product->{upgrades}->{upgrade}||[]} ) {
1182     $upgrade->{name} = $productname;
1183     $upgrade->{product} = $packagename;
1184   }
1185
1186   mkdir_p( "$outdir/_product:$packagename" ) || die ("Unable to create migration directory\n");
1187   writeMigrationSPECfile( "$outdir/_product:".$packagename."/".$packagename.".spec",
1188                           $prodRef, $product, $newpatchlevel, $product->{migrationtarget} );
1189 } elsif (defined($product->{patchlevel})){
1190   my $newpatchlevel = $product->{'patchlevel'} + 1;
1191   my $productname = $product->{name}."-SP".$newpatchlevel."-migration";
1192   my $packagename = $productname;
1193   $packagename =~ s/\./_/g;
1194
1195   # enforce correct upgrade product name
1196   for my $upgrade ( @{$product->{upgrades}->{upgrade}||[]} ) {
1197     $upgrade->{name} = $productname;
1198     $upgrade->{product} = $packagename;
1199   }
1200
1201   mkdir_p( "$outdir/_product:$productname" ) || die ("Unable to create migration directory\n");
1202   writeMigrationSPECfile( "$outdir/_product:".$productname."/".$productname.".spec",
1203                           $prodRef, $product, $newpatchlevel, undef);
1204 }
1205
1206 my $SPECtemplateFile;
1207 if ($infile =~ /(.*\/)(.+)$/) {
1208   $SPECtemplateFile = $1."/".$product->{name}."-release.spec";
1209 };
1210 if ( !$SPECtemplateFile || ! -e $SPECtemplateFile ) {
1211   if ($infile =~ /(.*\/)(.+)$/) {
1212     $SPECtemplateFile = "$1/release.spec";
1213   };
1214 };
1215 if ( ! $SPECtemplateFile || ! -e $SPECtemplateFile ) {
1216   $SPECtemplateFile="";
1217   print "No release template file $SPECtemplateFile exists --> generating SPEC file $product->{name}-release.spec automatically\n";
1218 }
1219 mkdir_p( "$outdir/_product:$product->{name}-release" ) || die ("Unable to create $outdir\n");
1220 writeProductSPECfile( "$outdir/_product:$product->{name}-release/$product->{name}-release.spec", $SPECtemplateFile, $prodRef, $product );
1221 my $ChangesFile;
1222 if ($infile =~ /(.*\/)(.+)$/) {
1223   $ChangesFile = $1."/".$product->{name}."-release.changes";
1224 };
1225 if ( !$ChangesFile || ! -e $ChangesFile ) {
1226   if ($infile =~ /(.*\/)(.+)$/) {
1227     $ChangesFile = "$1/release.changes";
1228   };
1229 };
1230 if ( defined($ChangesFile) && -e $ChangesFile ) {
1231   my $fn="$outdir/_product:$product->{name}-release/$product->{name}-release.changes";
1232   system( "cp", $ChangesFile, $fn) && die ("Unable to copy changes file $ChangesFile to $fn");
1233 }
1234
1235 #
1236 # Create kiwi images
1237 #
1238
1239 my %generalImage = %{$kiwiImage};
1240
1241 my $media = $prodRef->{mediasets}->{media};
1242
1243 if( $opt_m ) {
1244     print "Generating only media set $opt_m, due to commandline switch\n";
1245 }
1246
1247 foreach my $medium ( @$media ){
1248     my $type = $medium->{type};
1249     my $flavor = $medium->{flavor};
1250     my $product = $medium->{product};  # note: this needs to reference a product from the products section 
1251     $product = $prodRef->{'products'}->{'product'}[0]->{'name'} unless $product; # use global name as fallback
1252     my $name = $medium->{name};
1253
1254     # bug compatibility for 11.3 and before (ignoring product name definition per media)
1255     if ((not defined $medium->{'mediastyle'}) || $medium->{'mediastyle'} eq "suse-11.1" || $medium->{'mediastyle'} eq "suse-11.2" || $medium->{'mediastyle'} eq "suse-11.3") {
1256       $product = $prodRef->{'products'}->{'product'}[0]->{'name'};
1257       if ($medium->{'product'}) {
1258         print "WARNING: own product attribute \"product\" per media is used. This may create inconsistent medias!\n";
1259         $product = $medium->{'product'};
1260       }
1261     }
1262    
1263     next if( $opt_m && $name ne $opt_m );
1264
1265     # create one kiwi file each for every of the archsets
1266     if ( defined(@{$medium->{archsets}}) ) {
1267       my @archSets = @{$medium->{archsets}};
1268       foreach my $arch ( @archSets ) {
1269           my @archs;
1270       
1271           my $kiwi = Storable::dclone(\%generalImage);
1272           if (defined($medium->{'mediastyle'}) && $medium->{'mediastyle'} ne "suse-11.1" && $medium->{'mediastyle'} ne "suse-11.2") {
1273               $kiwi->{schemaversion} = "4.1"; # new kiwi requires exact this version
1274               $kiwi->{preferences}->{type} = [{image => "product"}];
1275           }else{
1276               # the schemE vs. schemA is intended by kiwi!
1277               $kiwi->{schemeversion} = "2.4"; # before openSUSE 11.3, until kiwi 3.74
1278               $kiwi->{preferences}->{type} = [{_content => "product"}];
1279           }
1280           $kiwi->{preferences}->{version} = "1.0.0"; # hardcoded to fullfill kiwi scheme, real version is defined elsewhere
1281           $kiwi->{preferences}->{packagemanager} = "zypper" ; # hardcoded since no other support exist yet.
1282       
1283           $kiwi->{instsource} = createInstsource ( $prodRef, $medium, $arch->{archset} );
1284           $kiwi->{repository} = createRepository ( $prodRef );
1285       
1286           my $archStr;
1287           my @archsets = @{$arch->{archset}};
1288           my @productarch;
1289           foreach my $ar ( @archsets ) {
1290               if( $archSets{$ar->{'ref'}} ) {
1291                   my $architecture = "$archSets{$ar->{'ref'}}->{'productarch'}";
1292                   $archStr .= "_" if $archStr;
1293                   $archStr .= "$architecture";
1294                   # enable this architecture in scheduler
1295                   # FIXME: the scheduler arch may have a different name than the rpm archs !
1296                   push @archs, { 'arch' => $architecture };
1297                   push @productarch, $architecture;
1298                   if ( defined($localarch) ) {
1299                      # this is only important on a server which supports a "local" scheduler
1300                      push @archs, { 'arch' => 'local' };
1301                   }
1302                   push @archs, { 'arch' => 'i586' } if ( $architecture eq "x86_64" );
1303                   push @archs, { 'arch' => 'i586' } if ( $architecture eq "ia64" );
1304                   push @archs, { 'arch' => 'ppc' } if ( $architecture eq "ppc64" );
1305                   push @archs, { 'arch' => 'ppc64' } if ( $architecture eq "ppc" ); # ppc is using ppc64 stuff in openSUSE
1306                   push @archs, { 'arch' => 's390' } if ( $architecture eq "s390x" );
1307               }
1308           }
1309
1310           # add implicit the release packages to media
1311           if ( $kiwi->{instsource}->{repopackages}[0] && (!defined($medium->{'skip_release_package'}) || $medium->{'skip_release_package'} ne "true") ){
1312             my %product_pack;
1313             my $addarch = join( ",", @productarch );
1314             my $name = $product."-release";
1315             push @{$kiwi->{instsource}->{repopackages}[0]->{repopackage}}, { "name" => $name, addarch => $addarch };
1316             # add the flavor package
1317             $name .= "-".$flavor;
1318             push @{$kiwi->{instsource}->{repopackages}[0]->{repopackage}}, { "name" => $name, addarch => $addarch };
1319           }
1320
1321           my $file = "$product-$type-$flavor-$archStr";
1322           die("illegal kiwi product: $file\n") if $file =~ /^[_\.]/;
1323           die("illegal kiwi product: $file\n") if $file =~ /[\/\000-\037]/;
1324       
1325           my $pkgName = "_product:$file";
1326           my $kiwiDir = "$outdir/$pkgName/";
1327           my $outFile = "$kiwiDir/$file.kiwi";
1328           my $metaFile= "$kiwiDir/_meta";
1329       
1330           mkdir_p( $kiwiDir ) || die ("Unable to create $kiwiDir\n");
1331           writexml( "$outFile$$", $outFile, $kiwi, $BSKiwiXML::kiwidesc );
1332
1333           # Create meta file to have a default bcntsynctag
1334           if ( $project ) {
1335             my $pkgmeta;
1336             $pkgmeta->{'name'} = $pkgName;
1337             $pkgmeta->{'project'} = $project;
1338             $pkgmeta->{'title'} = "KIWI image build" ;
1339             $pkgmeta->{'description'} = "Automatically generate from _product" ;
1340             $pkgmeta->{'bcntsynctag'} = "_product:".$product ;
1341             writexml( "$metaFile$$", $metaFile, $pkgmeta, $BSXML::pack );
1342           } else {
1343             print "metafile SKIPPED ! (need project name argument for it)\n";
1344           }
1345
1346           print "$outFile written.\n";
1347       }
1348     }
1349 }
1350
1351
1352
1353 # end