Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:adrianSuSE
build
writemodulemd
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File writemodulemd of Package build
#!/usr/bin/perl -w ################################################################ # # Copyright (c) 2021 SUSE Linux GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or 3 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program (see the file COPYING); if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # ################################################################ # modulemd writing support BEGIN { unshift @INC, ($::ENV{"BUILD_DIR"} || "/usr/lib/build"); } use strict; use Build::Rpm; use Build::Modulemd; my $filter_artifacts = 0; # taken from pungi's multilib definitions my %multilib = ( 'x86_64' => [ 'i686' ], ); sub printmd { my ($md) = @_; print Build::Modulemd::mdtoyaml($md); } sub readmds { my ($modulemdfile) = @_; my $mds; if ($modulemdfile =~ /\.pst$/) { require Storable; $mds = Storable::retrieve($modulemdfile); $mds = [ $mds ] if ref($mds) eq 'HASH'; } elsif ($modulemdfile =~ /\.ya?ml$/) { require YAML::XS; $YAML::XS::LoadBlessed = $YAML::XS::LoadBlessed = 0; $mds = [ YAML::XS::LoadFile($modulemdfile) ]; } else { die("unsupported modulemd file: $modulemdfile\n"); } die("no modulemd data\n") unless @$mds; for my $md (@$mds) { die("bad modulemd data\n") unless $md && ref($md) eq 'HASH' && $md->{'data'} && $md->{'document'}; die("unknown modulemd document\n") if $md->{'document'} ne 'modulemd' && $md->{'document'} ne 'modulemd' && $md->{'document'} ne 'modulemd-defaults'; die("bad modulemd version \n") if $md->{'document'} eq 'modulemd' && $md->{'version'} != 2; die("bad modulemd version \n") if $md->{'document'} eq 'modulemd-defaults' && $md->{'version'} != 1; } return $mds; } sub convertdeps { my ($d) = @_; my $nd = {}; for my $dd (@$d) { my ($n, @v) = split(':', $dd); $nd->{$n} = \@v; } return $nd; } sub unifyandsort { my %m = map {$_ => 1} @_; return [ sort keys %m ]; } sub sortarray { my ($d, @c) = @_; if (@c) { if (ref($d) eq 'ARRAY') { sortarray($_, @c) for @$d; } elsif (ref($d) eq 'HASH') { my $k = shift @c; if ($k eq '*') { sortarray($_, @c) for values %$d; } else { sortarray($d->{$k}, @c) if exists $d->{$k}; } } } elsif ($d && ref($d) eq 'ARRAY') { @$d = sort @$d; } } if (@ARGV && ($ARGV[0] eq '--filter' || $ARGV[0] eq 'filter')) { shift @ARGV; my %archfilter; while (@ARGV >= 2 && $ARGV[0] eq '--arch') { $archfilter{$ARGV[1]} = 1; splice(@ARGV, 0, 2); } die("usage: writemodulemd --filter [--arch <ARCH>] <modulemdfile> <primaryfile>\n") unless @ARGV == 2; my ($modulemdfile, $primaryfile) = @ARGV; my $mds = readmds($modulemdfile); require Build::Rpmmd; if ($primaryfile ne '-' && -d $primaryfile) { die("$primaryfile/repomd.xml: $!\n") unless -e "$primaryfile/repomd.xml"; # primaryfile is repomd directory my $files = Build::Rpmmd::parse_repomd("$primaryfile/repomd.xml"); my @primaryfiles = grep {$_->{'type'} eq 'primary' && defined($_->{'location'})} @{$files || []}; die("no primary files in repodata\n") unless @primaryfiles; @primaryfiles = grep {$_->{'location'} =~ /\.xml(?:\.xz|\.gz)?$/} @primaryfiles; die("cannot decompress primary file\n") unless @primaryfiles; my $loc = $primaryfiles[0]->{'location'}; $loc =~ s/.*\///; $primaryfile .= "/$loc"; } if ($primaryfile ne '-') { if ($primaryfile =~ /\.gz$/) { open(STDIN, '-|', 'gunzip', '-dc', '--', $primaryfile) || die("$primaryfile: $!\n"); } elsif ($primaryfile =~ /\.xz$/) { open(STDIN, '-|', 'xzdec', '-dc', '--', $primaryfile) || die("$primaryfile: $!\n"); } else { open(STDIN, '<', $primaryfile) || die("$primaryfile: $!\n"); } } my %rpms; Build::Rpmmd::parse(\*STDIN, sub { my ($r) = @_; my $evr = ($r->{'epoch'} || 0).":$r->{'version'}-$r->{'release'}"; $rpms{"$r->{'name'}-$evr.$r->{'arch'}"} = [ $r->{'source'}, $r->{'license'} ]; }, 'withlicense' => 1); my %outmds; my %havens; my %multiartifacts; my %multilicenses; for my $md (@$mds) { my $mdd = $md->{'data'}; if ($md->{'document'} eq 'modulemd-defaults') { my $ns = "$mdd->{'module'}:$mdd->{'stream'}"; $outmds{$ns} = $md; next; } # apply rpm name filtering if ($mdd->{'artifacts'} && $mdd->{'artifacts'}->{'rpms'} && $mdd->{'filter'} && $mdd->{'filter'}->{'rpms'}) { my %filter = map {$_ => 1} @{$mdd->{'filter'}->{'rpms'}}; my $havebinaryrpms; for (splice @{$mdd->{'artifacts'}->{'rpms'}}) { if (/\.(?:no)?src$/) { push @{$mdd->{'artifacts'}->{'rpms'}}, $_; next; } next unless /(.*)-[^-]*-[^-]*\.[^\.]+$/; next if $filter{$1}; my $n = $1; $n =~ s/-debug(?:info|source)$//; next if $filter{$n}; push @{$mdd->{'artifacts'}->{'rpms'}}, $_; $havebinaryrpms = 1; } @{$mdd->{'artifacts'}->{'rpms'}} = () unless $havebinaryrpms; } # check if we have this md in the rpms if ($mdd->{'artifacts'}) { my %components; if ($mdd->{'buildopts'} && $mdd->{'buildopts'}->{'whitelist'}) { %components = map {$_ => 1} @{$mdd->{'buildopts'}->{'whitelist'}}; } elsif ($mdd->{'components'} && $mdd->{'components'}->{'rpms'}) { %components = map {$_ => 1} keys %{$mdd->{'components'}->{'rpms'}}; } next unless grep {exists($rpms{$_}) && $components{$rpms{$_}->[0]}} @{$mdd->{'artifacts'}->{'rpms'} || []}; } # filter artifacts and licenses if ($filter_artifacts && $mdd->{'artifacts'}) { my @have = grep {exists $rpms{$_}} @{$mdd->{'artifacts'}->{'rpms'} || []}; my %licenses; for (@have) { $licenses{$rpms{$_}->[1]} = 1 if $rpms{$_} && defined $rpms{$_}->[1]; } if (@have) { $mdd->{'artifacts'}->{'rpms'} = \@have; } else { delete $mdd->{'artifacts'}->{'rpms'}; delete $mdd->{'artifacts'} unless %{$mdd->{'artifacts'} || {}}; } if (%licenses) { $mdd->{'license'}->{'content'} = [ sort keys %licenses ]; } else { delete $mdd->{'license'}->{'content'} if $mdd->{'license'} && $mdd->{'license'}->{'content'}; } } if ($mdd->{'components'} && $mdd->{'components'}->{'rpms'} && $mdd->{'artifacts'} && $mdd->{'artifacts'}->{'rpms'}) { my %havearch; my %havesrc; for (@{$mdd->{'artifacts'}->{'rpms'} || []}) { next unless /(.*)-[^-]*-[^-]*\.([^\.]+)$/; $havearch{$2} = 1; $havesrc{$1} = 1 if $2 eq 'src' || $2 eq 'nosrc'; } my %domulti; for my $sname (sort keys %havesrc) { my $c = $mdd->{'components'}->{'rpms'}->{$sname}; next unless $c; for my $march (@{$c->{'multilib'} || []}) { $domulti{$_}->{$march} = 1 for grep {$havearch{$_}} @{$multilib{$march} || []}; } } for my $v (values %domulti) { $domulti{'src'}->{$_} = $domulti{'nosrc'}->{$_} = 1 for keys %$v; } if (%domulti) { for (@{$mdd->{'artifacts'}->{'rpms'} || []}) { next unless /.*-[^-]*-[^-]*\.([^\.]+)$/; for my $march (sort keys %{$domulti{$1} || {}}) { my $label = "$mdd->{'name'}:$mdd->{'stream'}:$mdd->{'version'}:$mdd->{'context'}:$march"; push @{$multiartifacts{$label}}, $_; push @{$multilicenses{$label}}, @{$mdd->{'license'}->{'content'}} if $mdd->{'license'} && @{$mdd->{'license'}->{'content'}}; } } } } next if %archfilter && !$archfilter{$mdd->{'arch'}}; my $ns = "$mdd->{'name'}:$mdd->{'stream'}"; $havens{$ns} = 1; my $label = "$mdd->{'name'}:$mdd->{'stream'}:$mdd->{'version'}:$mdd->{'context'}:$mdd->{'arch'}"; my $omd = $outmds{$label}; if ($omd) { # merge with existing entry my $omdd = $omd->{'data'}; ($md, $omd, $mdd, $omdd) = ($omd, $md, $omdd, $mdd) if $omdd->{'version'} > $mdd->{'version'}; my %sources; my %osources; if ($mdd->{'artifacts'} && $mdd->{'artifacts'}->{'rpms'}) { for (@{$mdd->{'artifacts'}->{'rpms'}}) { $sources{$1} = 1 if /(.*)-[^-]*-[^-]*\.([^\.]+)$/; } } if ($omdd->{'artifacts'} && $omdd->{'artifacts'}->{'rpms'}) { for (@{$omdd->{'artifacts'}->{'rpms'}}) { $osources{$1} = 1 if /(.*)-[^-]*-[^-]*\.([^\.]+)$/; } } if ($mdd->{'components'} || $omdd->{'components'}) { my $components = $mdd->{'components'}->{'rpms'} || {}; for my $s (sort keys %{$omdd->{'components'}->{'rpms'} || {}}) { $components->{$s} = $omdd->{'components'}->{'rpms'}->{$s} if !$components->{$s} || (!$sources{$s} && $osources{$s}); } delete $mdd->{'components'}->{'rpms'}; $mdd->{'components'}->{'rpms'} = $components if %$components; } if ($mdd->{'artifacts'} || $omdd->{'artifacts'}) { my $artifacts = unifyandsort(@{$mdd->{'artifacts'}->{'rpms'} || []}, @{$omdd->{'artifacts'}->{'rpms'} || []}); delete $mdd->{'artifacts'}->{'rpms'}; $mdd->{'artifacts'}->{'rpms'} = $artifacts if @$artifacts; } if ($mdd->{'license'} || $omdd->{'license'}) { my $licenses = unifyandsort(@{$mdd->{'license'}->{'content'} || []}, @{$omdd->{'license'}->{'content'} || []}); delete $mdd->{'license'}->{'content'}; $mdd->{'license'}->{'content'} = $licenses if @$licenses; } } $outmds{$label} = $md; } # merge multilib entries for my $label (sort keys %multiartifacts) { my $md = $outmds{$label}; next unless $md; my $mdd = $md->{'data'}; my $artifacts = unifyandsort(@{$mdd->{'artifacts'}->{'rpms'} || []}, @{$multiartifacts{$label}}); $mdd->{'artifacts'}->{'rpms'} = $artifacts; if ($multilicenses{$label}) { my $licenses = unifyandsort(@{$mdd->{'license'}->{'content'} || []}, @{$multilicenses{$label}}); $mdd->{'license'}->{'content'} = $licenses; } } # now dump em all for (sort keys %outmds) { my $md = $outmds{$_}; if ($md->{'document'} eq 'modulemd-defaults') { my $mdd = $md->{'data'}; my $ns = "$mdd->{'module'}:$mdd->{'stream'}"; next unless $havens{$ns}; } # normalize some entries sortarray($md, 'data', 'profiles', '*', 'rpms'); sortarray($md, 'data', 'artifacts', 'rpms'); sortarray($md, 'data', 'license', 'content'); sortarray($md, 'data', 'buildopts', 'rpms', 'whitelist'); sortarray($md, 'data', 'filter', 'rpms'); sortarray($md, 'data', 'api', 'rpms'); printmd($md); } exit; } if (@ARGV && ($ARGV[0] eq '--convert' || $ARGV[0] eq 'convert')) { shift @ARGV; die("usage: writemodulemd --convert <modulemdfile>\n") unless @ARGV; my @mds; for my $modulemdfile (@ARGV) { push @mds, @{readmds($modulemdfile)}; } printmd($_) for @mds; exit; } if (@ARGV && ($ARGV[0] eq '--converttopst' || $ARGV[0] eq 'converttopst')) { shift @ARGV; die("usage: writemodulemd --converttopst <modulemdfile>\n") unless @ARGV; my @mds; for my $modulemdfile (@ARGV) { push @mds, @{readmds($modulemdfile)}; } require Storable; Storable::nstore_fd(\@mds, \*STDOUT); exit } my $disturl; (undef, $disturl) = splice(@ARGV, 0, 2) if @ARGV >= 2 && $ARGV[0] eq '--disturl'; die("usage: writemodulemd [--disturl <disturl] <modulemdfile> <rpmmanifestfile>\n") unless @ARGV == 2; my ($modulemdfile, $manifestfile) = @ARGV; my $mds = readmds($modulemdfile); my @mds_good = grep {$_->{'document'} eq 'modulemd'} @$mds; die("need exactly one modulemd document\n") unless @mds_good == 1; my $md = $mds_good[0]; my $mdd = $md->{'data'}; # convert deps if needed (to be removed) for my $d (@{$mdd->{'dependencies'} || []}) { $d->{'requires'} = convertdeps($d->{'requires'}) if ref($d->{'requires'}) eq 'ARRAY'; $d->{'buildrequires'} = convertdeps($d->{'buildrequires'}) if ref($d->{'buildrequires'}) eq 'ARRAY'; } delete $mdd->{'artifacts'}; delete $mdd->{'license'}->{'content'} if $mdd->{'license'} && $mdd->{'license'}->{'content'}; if ($manifestfile ne '-') { open(STDIN, '<', $manifestfile) || die("$manifestfile: $!\n"); } my %licenses; my %sources; while (<STDIN>) { chomp; my $r = Build::Rpm::query($_, 'evra' => 1, 'license' => 1); next if $r->{'name'} eq 'empty-modulemd-hack'; $sources{$r->{'name'}} = 1 if $r->{'arch'} eq 'src' || $r->{'arch'} eq 'nosrc'; $r->{'epoch'} ||= 0; my $nevra = "$r->{'name'}-$r->{'epoch'}:$r->{'version'}-$r->{'release'}.$r->{'arch'}"; my $license = $r->{'license'}; $licenses{$license} = 1 if $license; push @{$mdd->{'artifacts'}->{'rpms'}}, $nevra; } $mdd->{'license'}->{'content'} = [ sort keys %licenses ] if %licenses; if ($disturl && %sources && $mdd->{'components'} && $mdd->{'components'}->{'rpms'}) { for my $s (sort keys %sources) { my $c = $mdd->{'components'}->{'rpms'}->{$s}; $c->{'ref'} = $disturl if $c; } } printmd($_) for @$mds;
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor