Pile of OSGi dependency generator fixes from Alphonse Van Assche (ticket #39)

- use Temp perl module to provide temp dir
- re-enable deps solving in Require-Bundle, Import-Package, Export-Package
  OSGI properties
- remove uses bundle of Export-Package OSGI property
- use RPM '>=' as version operator to match OSGI '='
- remove all .0 at the end of the version string
- typo fixes
This commit is contained in:
Panu Matilainen 2009-06-18 13:06:42 +03:00
parent d5f6d18540
commit 7417cd283a
1 changed files with 316 additions and 162 deletions

View File

@ -1,203 +1,357 @@
#!/usr/bin/perl #!/usr/bin/perl
#
# osgideps.pl -- Analyze dependencies of OSGi bundles.
#
# Kyu Lee
# Alphonse Van Assche <alcapcom@fedoraproject.org>
#
# $Id: osgideps.pl,v 1.0 2009/06/08 12:12:12 mej Exp $
#
use Cwd; use Cwd;
use Getopt::Long; use Getopt::Long;
use File::Temp qw/ tempdir /;
$cdir = getcwd(); $cdir = getcwd();
$TEMPDIR="/tmp"; $TEMPDIR = tempdir( CLEANUP => 1 );
$MANIFEST_NAME="META-INF/MANIFEST.MF"; $MANIFEST_NAME = "META-INF/MANIFEST.MF";
# prepare temporary directory # prepare temporary directory
if (! (-d $TEMPDIR)) { if ( !( -d $TEMPDIR ) ) {
if (($_ = `mkdir $TEMPDIR`) != 0) {exit 1;} if ( ( $_ = `mkdir $TEMPDIR` ) != 0 ) { exit 1; }
elsif (! (-w $TEMPDIR) && (-x $TEMPDIR)) {exit 1;} elsif ( !( -w $TEMPDIR ) && ( -x $TEMPDIR ) ) { exit 1; }
} }
# parse options # parse options
my ($show_provides, $show_requires); my ( $show_provides, $show_requires, $show_system_bundles, $debug );
my $result = GetOptions(
my $result = GetOptions("provides" => \$show_provides, "provides" => \$show_provides,
"requires" => \$show_requires); "requires" => \$show_requires,
"system" => \$show_system_bundles,
exit(1) if (not $result); "debug" => \$debug
);
exit(1) if ( not $result );
# run selected function
@allfiles = <STDIN>; @allfiles = <STDIN>;
if ($show_provides) { if ($show_provides) {
do_provides(@allfiles); getProvides(@allfiles);
} }
if ($show_requires) { if ($show_requires) {
do_requires(@allfiles); getRequires(@allfiles);
}
if ($show_system_bundles) {
getSystemBundles(@allfiles);
} }
exit(0); exit(0);
# this function print provides of OSGi aware files
sub getProvides {
sub do_provides {
foreach $file (@_) {
next if -f $file && -r $file && !-l $file;
$file =~ s/[^[:print:]]//g;
if ($file =~ m/$MANIFEST_NAME$/ || $file =~ m/\.jar$/ ) {
if ($file =~ m/\.jar$/) {
# if this jar contains MANIFEST.MF file
if (`jar tf $file | grep -e \^$MANIFEST_NAME` eq "$MANIFEST_NAME\n") {
# extract MANIFEST.MF file from jar to temporary directory
chdir $TEMPDIR;
`jar xf $file $MANIFEST_NAME`;
open(MANIFEST, "$MANIFEST_NAME");
chdir $cdir;
}
} else {
open(MANIFEST, "$file");
}
my $bundleName = "";
my $version = "";
# parse bundle name and version
while(<MANIFEST>) {
# get rid of non-print chars (some manifest files contain weird chars)
s/[^[:print]]//g;
if (m/(^Bundle-SymbolicName: )((\w|\.)+)(\;*)(.*\n)/) {
$bundleName = $2;
}
if (m/(^Bundle-Version: )(.*)/) {
$version = $2;
$version = fixVersion($version);
}
if (m/(^(Export-Package): )(.*)$/) {
my $bunlist = "$3"."\n";
while(<MANIFEST>) {
if (m/^[[:upper:]][[:alpha:]]+-[[:upper:]][[:alpha:]]+: .*/) {
$len = length $_;
seek MANIFEST, $len*-1 , 1;
last;
}
$bunlist.="$_";
}
push @bundlelist, parsePkgString($bunlist);
}
}
# skip this jar if no bundle name exists
if (! $bundleName eq "") {
if (! $version eq "") {
print "osgi(".$bundleName.") = ".$version."\n";
} else {
print "osgi(".$bundleName.")\n";
}
}
}
}
$list = "";
for $bundle (@bundlelist) {
$list .= "osgi(".$bundle->{NAME}.")".$bundle->{VERSION}."\n";
}
# For now we dont take Require-Bundle AND Import-Package in account
#print $list;
}
sub do_requires {
foreach $file (@_) { foreach $file (@_) {
chomp($file);
next if -f $file && -r $file; # we don't follow symlinks for provides
next if -f $file && -r $file && -l $file;
$file =~ s/[^[:print:]]//g; $file =~ s/[^[:print:]]//g;
if ($file =~ m/$MANIFEST_NAME$/ || $file =~ m/\.jar$/ ) { if ( $file =~ m/$MANIFEST_NAME$/ || $file =~ m/\.jar$/ ) {
if ($file =~ m/\.jar$/) { if ( $file =~ m/\.jar$/ ) {
# if this jar contains MANIFEST.MF file if ( `jar tf $file | grep -e \^$MANIFEST_NAME` eq "$MANIFEST_NAME\n" ) {
if (`jar tf $file | grep -e \^$MANIFEST_NAME` eq "$MANIFEST_NAME\n") {
# extract MANIFEST.MF file from jar to temporary directory # extract MANIFEST.MF file from jar to temporary directory
chdir $TEMPDIR; chdir $TEMPDIR;
`jar xf $file $MANIFEST_NAME`; `jar xf $file $MANIFEST_NAME`;
open(MANIFEST, "$MANIFEST_NAME"); open( MANIFEST, "$MANIFEST_NAME" );
chdir $cdir; chdir $cdir;
} }
} else { } else {
open(MANIFEST, "$file"); open( MANIFEST, "$file" );
} }
my %reqcomp = (); my $bundleName = "";
while(<MANIFEST>) { my $version = "";
if (m/(^(Require-Bundle|Import-Package): )(.*)$/) { # parse Bundle-SymbolicName, Bundle-Version and Export-Package attributes
my $bunlist = "$3"."\n"; while (<MANIFEST>) {
while(<MANIFEST>) { # get rid of non-print chars (some manifest files contain weird chars)
if (m/^[[:upper:]][[:alpha:]]+-[[:upper:]][[:alpha:]]+: .*/) { s/[^[:print]]//g;
$len = length $_; if ( m/(^(Bundle-SymbolicName): )(.*)$/ ) {
seek MANIFEST, $len*-1 , 1; $bundleName = "$3" . "\n";
last; while (<MANIFEST>) {
} if ( m/^[[:upper:]][[:alpha:]]+-[[:upper:]][[:alpha:]]+: .*/ ) {
$bunlist.="$_"; $len = length $_;
} seek MANIFEST, $len * -1, 1;
push @bundlelist, parsePkgString($bunlist); last;
} }
} $bundleName .= "$_";
}
$bundleName =~ s/\s+//g;
$bundleName =~ s/;.*//g;
}
if ( m/(^Bundle-Version: )(.*)/ ) {
$version = $2;
}
if ( m/(^(Export-Package): )(.*)$/ ) {
my $bunlist = "$3" . "\n";
while (<MANIFEST>) {
if ( m/^[[:upper:]][[:alpha:]]+-[[:upper:]][[:alpha:]]+: .*/ ) {
$len = length $_;
seek MANIFEST, $len * -1, 1;
last;
}
$bunlist .= "$_";
}
push @bundlelist, parsePkgString($bunlist, $file);
}
}
# skip this jar if no bundle name exists
if ( !$bundleName eq "" ) {
if ( !$version eq "" ) {
$version = parseVersion($version);
push @bundlelist, { FILE => "$file", NAME => "$bundleName", VERSION => "$version" };
} else {
push @bundlelist, { FILE => "$file", NAME => "$bundleName", VERSION => "" };
}
}
}
} }
if ( !$debug ) { @bundlelist = prepareOSGiBundlesList(@bundlelist); }
$list = "";
for $bundle (@bundlelist) {
if ( !$debug ) {
$list .= "osgi(" . $bundle->{NAME} . ")" . $bundle->{VERSION} . "\n";
} else {
$list .= $bundle->{FILE} . " osgi(" . $bundle->{NAME} . ")" . $bundle->{VERSION} . "\n";
}
}
print $list;
} }
$list = ""; # this function print requires of OSGi aware files
for $bundle (@bundlelist) { sub getRequires {
$list .= "osgi(".$bundle->{NAME}.")".$bundle->{VERSION}."\n"; foreach $file (@_) {
next if (-f $file && -r $file);
# we explicitly requires symlinked jars
if (-l $file) {
$file = readlink $file;
if ( !$file eq "" ) {
print "$file" . "\n";
}
next;
}
$file =~ s/[^[:print:]]//g;
if ( $file =~ m/$MANIFEST_NAME$/ || $file =~ m/\.jar$/ ) {
if ( $file =~ m/\.jar$/ ) {
if ( `jar tf $file | grep -e \^$MANIFEST_NAME` eq "$MANIFEST_NAME\n" ) {
# extract MANIFEST.MF file from jar to temporary directory
chdir $TEMPDIR;
`jar xf $file $MANIFEST_NAME`;
open( MANIFEST, "$MANIFEST_NAME" );
chdir $cdir;
}
} else {
open( MANIFEST, "$file" );
}
while (<MANIFEST>) {
if ( m/(^(Require-Bundle|Import-Package): )(.*)$/ ) {
my $bunlist = "$3" . "\n";
while (<MANIFEST>) {
if (m/^[[:upper:]][[:alpha:]]+-[[:upper:]][[:alpha:]]+: .*/ ) {
$len = length $_;
seek MANIFEST, $len * -1, 1;
last;
}
$bunlist .= "$_";
}
push @bundlelist, parsePkgString($bunlist, $file);
}
# we also explicitly require symlinked jars define by
# Bundle-ClassPath attribut
if ( m/(^(Bundle-ClassPath): )(.*)$/ ) {
$bunclp = "$3" . "\n";
while (<MANIFEST>) {
if ( m/^[[:upper:]][[:alpha:]]+-[[:upper:]][[:alpha:]]+: .*/ ) {
$len = length $_;
seek MANIFEST, $len * -1, 1;
last;
}
$bunclp .= "$_";
}
$bunclp =~ s/\ //g;
$bunclp =~ s/\n//g;
$bunclp =~ s/[^[:print:]]//g;
$dir = `dirname $file`;
$dir =~ s/\n//g;
@jars = split /,/, $bunclp;
for $jarfile (@jars) {
$jarfile = "$dir\/\.\.\/$jarfile";
$jarfile = readlink $jarfile;
if ( !$jarfile eq "" ) {
print "$jarfile" . "\n";
}
}
}
}
}
}
if ( !$debug ) { @bundlelist = prepareOSGiBundlesList(@bundlelist); }
$list = "";
for $bundle (@bundlelist) {
# replace '=' by '>=' because qualifiers are set on provides
# but not on requires.
$bundle->{VERSION} =~ s/\ =/\ >=/g;
if ( !$debug ) {
$list .= "osgi(" . $bundle->{NAME} . ")" . $bundle->{VERSION} . "\n";
} else {
$list .= $bundle->{FILE} . " osgi(" . $bundle->{NAME} . ")" . $bundle->{VERSION} . "\n";
}
}
print $list;
} }
# For now we dont take Require-Bundle AND Import-Package in account
#print $list; # this function print system bundles of OSGi profile files.
sub getSystemBundles {
foreach $file (@_) {
if ( -r $file && -r $file ) {
print "'$file' file not found or cannot be read!";
next;
} else {
open( PROFILE, "$file" );
while (<PROFILE>) {
if ( $file =~ m/\.profile$/ ) {
if (m/(^(org\.osgi\.framework\.system\.packages)[=|\ ]+)(.*)$/) {
$syspkgs = "$3" . "\n";
while (<PROFILE>) {
if (m/^[a-z]/) {
$len = length $_;
seek MANIFEST, $len * -1, 1;
last;
}
$syspkgs .= "$_";
}
$syspkgs =~ s/\s+//g;
$syspkgs =~ s/\\//g;
@bundles = split /,/, $syspkgs;
foreach $bundle (@bundles) {
print "osgi(" . $bundle . ")\n";
}
}
}
}
}
}
} }
sub parsePkgString { sub parsePkgString {
my $bunstr = $_[0]; my $bunstr = $_[0];
my @return; my $file = $_[1];
my @return;
$bunstr =~ s/ //g; $bunstr =~ s/ //g;
$bunstr =~ s/\n//g; $bunstr =~ s/\n//g;
$bunstr =~ s/[^[:print:]]//g; $bunstr =~ s/[^[:print:]]//g;
$bunstr =~ s/("[[:alnum:]|\-|\_|\.|\(|\)|\[|\]]+)(,)([[:alnum:]|\-|\_|\.|\(|\)|\[|\]]+")/$1 $3/g; $bunstr =~ s/("[[:alnum:]|\-|\_|\.|\(|\)|\[|\]]+)(,)([[:alnum:]|\-|\_|\.|\(|\)|\[|\]]+")/$1 $3/g;
@reqcomp = split /,/g, $bunstr; # remove uses bundle from Export-Package attribute
foreach $reqelement (@reqcomp) { $bunstr =~ s/uses:="[[:alnum:]|\-|\_|\.|\(|\)|\[|\]|,]+"//g;
@reqelementfrmnt = split /;/g, $reqelement; # remove optional dependencies
$name=""; $bunstr =~ s/,.*;resolution:=optional//g;
$version=""; # remove x-friends
$name = $reqelementfrmnt[0]; $bunstr =~ s/;x-friends:="[[:alnum:]|\-|\_|\.|\(|\)|\[|\]|,]+"//g;
for $i (1 .. $#reqelementfrmnt) { # remove signatures
if ($reqelementfrmnt[$i] =~ m/(^(bundle-|)version=")(.*)(")/){ $bunstr =~ s/Name:.*SHA1-Digest:.*//g;
$version = $3; @reqcomp = split /,/, $bunstr;
last; foreach $reqelement (@reqcomp) {
} @reqelementfrmnt = split /;/, $reqelement;
} $name = "";
$version = parseVersion($version); $version = "";
$version = fixVersion($version); $name = $reqelementfrmnt[0];
# dirty fix for provides that contain " char
$name =~ s/\"//g; $name =~ s/\"//g;
push @return, { NAME=>"$name", VERSION=>"$version"}; # ignore 'system.bundle'.
} # see http://help.eclipse.org/stable/index.jsp?topic=/org.eclipse.platform.doc.isv/porting/3.3/incompatibilities.html
next if ( $name =~ m/^system\.bundle$/ );
return @return; for $i ( 1 .. $#reqelementfrmnt ) {
if ( $reqelementfrmnt[$i] =~ m/(^(bundle-|)version=")(.*)(")/ ) {
$version = $3;
last;
}
}
$version = parseVersion($version);
push @return, { FILE => "$file", NAME => "$name", VERSION => "$version" };
}
return @return;
} }
sub parseVersion { sub parseVersion {
my $ver = $_[0]; my $ver = $_[0];
if ($ver eq "") { return "";} if ( $ver eq "" ) { return ""; }
if ($ver =~ m/(^[\[|\(])(.+)\ (.+)([\]|\)]$)/) { if ( $ver =~ m/(^[\[|\(])(.+)\ (.+)([\]|\)]$)/ ) {
# FIXME: The right rpm match of osgi version [1,2) seems to be <= 2 if ( $1 eq "\[" ) {
# but when you look at the requires >= look more permssive/correct? $ver = " >= $2";
($1 eq "\[") ? return " >= $2" : return " > $2"; } else {
} else { $ver = " > $2";
return " = $ver"; }
} } else {
return $ver; $ver = " = $ver";
}
# we always return a full OSGi version to be able to match 1.0
# and 1.0.0 as equal in RPM.
( $major, $minor, $micro, $qualifier ) = split( '\.', $ver );
if ( !defined($minor) || !$minor ) {
$minor = 0;
}
if ( !defined($micro) || !$micro ) {
$micro = 0;
}
if ( !defined($qualifier) || !$qualifier ) {
$qualifier = "";
} else {
$qualifier = "." . $qualifier;
}
$ver = $major . "." . $minor . "." . $micro . $qualifier;
return $ver;
} }
sub fixVersion { # this function put the max version on each bundles to be able to remove
my $version = $_[0]; # duplicate deps with 'sort -u' command.
# remove version qualifier. sub prepareOSGiBundlesList {
$version =~ s/\.v.[0-9]*.*//g; foreach $bundle (@_) {
# We try to match RPM version, so remove last .0 foreach $cmp (@_) {
$version =~ s/\.0$//g; if ( $bundle->{NAME} eq $cmp->{NAME} ) {
return $version; $result = compareVersion( $bundle->{VERSION}, $cmp->{VERSION} );
if ( $result < 0 ) {
$bundle->{VERSION} = $cmp->{VERSION};
}
}
}
}
return @_;
} }
# this function returns a negative integer, zero, or a positive integer if
# $ver1 is less than, equal to, or greater than $ver2.
#
# REMEMBER: we mimic org.osgi.framework.Version#compareTo method but
# *at this time* we don't take care of the qualifier part of the version.
sub compareVersion {
my $ver1 = $_[0];
my $ver2 = $_[1];
$ver1 = "0.0.0" if ( $ver1 eq "" );
$ver2 = "0.0.0" if ( $ver2 eq "" );
$ver1 =~ m/([0-9]+)(\.)([0-9]+)(\.)([0-9]+)/;
$major1 = $1;
$minor1 = $3;
$micro1 = $5;
$ver2 =~ m/([0-9]+)(\.)([0-9]+)(\.)([0-9]+)/;
$major2 = $1;
$minor2 = $3;
$micro2 = $5;
$result = $major1 - $major2;
return $result if ( $result != 0 );
$result = $minor1 - $minor2;
return $result if ( $result != 0 );
$result = $micro1 - $micro2;
return $result if ( $result != 0 );
return $result;
}