Pile of OSGi dependency generator fixes from Alphonse Van Assche (ticket #39)
authorPanu Matilainen <pmatilai@redhat.com>
Thu, 18 Jun 2009 10:06:42 +0000 (13:06 +0300)
committerPanu Matilainen <pmatilai@redhat.com>
Tue, 21 Jul 2009 06:41:38 +0000 (09:41 +0300)
- 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
(cherry picked from commit 7417cd283a3641ee62b282ae36c5d2d7df349d04)

scripts/osgideps.pl

index 7b02016..971dade 100644 (file)
 #!/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 Getopt::Long;
-
+use File::Temp qw/ tempdir /;
 
 $cdir = getcwd();
-$TEMPDIR="/tmp";
-$MANIFEST_NAME="META-INF/MANIFEST.MF";
-
+$TEMPDIR = tempdir( CLEANUP => 1 );
+$MANIFEST_NAME = "META-INF/MANIFEST.MF";
 
 # prepare temporary directory
-if (! (-d $TEMPDIR)) {
-        if (($_ = `mkdir $TEMPDIR`) != 0) {exit 1;}
-        elsif (! (-w $TEMPDIR) && (-x $TEMPDIR)) {exit 1;}
+if ( !( -d $TEMPDIR ) ) {
+       if ( ( $_ = `mkdir $TEMPDIR` ) != 0 ) { exit 1; }
+       elsif ( !( -w $TEMPDIR ) && ( -x $TEMPDIR ) ) { exit 1; }
 }
 
 # parse options
-my ($show_provides, $show_requires);
-
-my $result = GetOptions("provides" => \$show_provides,
-                       "requires" => \$show_requires);
-
-exit(1) if (not $result);
-
-
+my ( $show_provides, $show_requires, $show_system_bundles, $debug );
+my $result = GetOptions(
+       "provides" => \$show_provides,
+       "requires" => \$show_requires,
+       "system" => \$show_system_bundles,
+       "debug" => \$debug
+);
+exit(1) if ( not $result );
 
+# run selected function
 @allfiles = <STDIN>;
-
 if ($show_provides) {
-       do_provides(@allfiles);
+       getProvides(@allfiles);
 }
-
 if ($show_requires) {
-       do_requires(@allfiles);
+       getRequires(@allfiles);
+}
+if ($show_system_bundles) {
+       getSystemBundles(@allfiles);
 }
-
-
 exit(0);
 
-
-
-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;
+# this function print provides of OSGi aware files
+sub getProvides {
+       foreach $file (@_) {
+               chomp($file);
+               # we don't follow symlinks for provides
+               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 ( `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" );
                        }
-               } 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);
+                       my $bundleName = "";
+                       my $version = "";
+                       # parse Bundle-SymbolicName, Bundle-Version and Export-Package attributes
+                       while (<MANIFEST>) {
+                               # get rid of non-print chars (some manifest files contain weird chars)
+                               s/[^[:print]]//g;
+                               if ( m/(^(Bundle-SymbolicName): )(.*)$/ ) {
+                                       $bundleName = "$3" . "\n";
+                                       while (<MANIFEST>) {
+                                               if ( m/^[[:upper:]][[:alpha:]]+-[[:upper:]][[:alpha:]]+: .*/ ) {
+                                                       $len = length $_;
+                                                       seek MANIFEST, $len * -1, 1;
+                                                       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);
+                               }
                        }
-                       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.="$_";
+
+                       # 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 => "" };
                                }
-                               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;
+       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;
 }
 
-
-sub do_requires {
-
+# this function print requires of OSGi aware files
+sub getRequires {
        foreach $file (@_) {
-
-               next if -f $file && -r $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 this jar contains MANIFEST.MF file
-                               if (`jar tf $file | grep -e \^$MANIFEST_NAME` eq "$MANIFEST_NAME\n") {
+               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 $TEMPDIR;
+                                       `jar xf $file $MANIFEST_NAME`;
+                                       open( MANIFEST, "$MANIFEST_NAME" );
                                        chdir $cdir;
                                }
-                       } else  {
-                               open(MANIFEST, "$file");
+                       } 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";
+                                               }
+                                       }
+                               }
                        }
-                               my %reqcomp = ();
-                       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);
-                        }
-                }
+               }
        }
+       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;
 }
 
-$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;
+# 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 {
-        my $bunstr = $_[0];
-        my @return;
+       my $bunstr = $_[0];
+       my $file = $_[1];
+       my @return;
        $bunstr =~ s/ //g;
-        $bunstr =~ s/\n//g;
-        $bunstr =~ s/[^[:print:]]//g;
-        $bunstr =~ s/("[[:alnum:]|\-|\_|\.|\(|\)|\[|\]]+)(,)([[:alnum:]|\-|\_|\.|\(|\)|\[|\]]+")/$1 $3/g;
-        @reqcomp = split /,/g, $bunstr;
-        foreach $reqelement (@reqcomp) {
-                @reqelementfrmnt = split /;/g, $reqelement;
-                $name="";
-                $version="";
-                $name = $reqelementfrmnt[0];
-                for $i (1 .. $#reqelementfrmnt) {
-                        if ($reqelementfrmnt[$i] =~ m/(^(bundle-|)version=")(.*)(")/){
-                                $version = $3;
-                                last;
-                        }
-                }
-                $version = parseVersion($version);
-               $version = fixVersion($version);
-               # dirty fix for provides that contain " char
+       $bunstr =~ s/\n//g;
+       $bunstr =~ s/[^[:print:]]//g;
+       $bunstr =~ s/("[[:alnum:]|\-|\_|\.|\(|\)|\[|\]]+)(,)([[:alnum:]|\-|\_|\.|\(|\)|\[|\]]+")/$1 $3/g;
+       # remove uses bundle from Export-Package attribute
+       $bunstr =~ s/uses:="[[:alnum:]|\-|\_|\.|\(|\)|\[|\]|,]+"//g;
+       # remove optional dependencies
+       $bunstr =~ s/,.*;resolution:=optional//g;
+       # remove x-friends
+       $bunstr =~ s/;x-friends:="[[:alnum:]|\-|\_|\.|\(|\)|\[|\]|,]+"//g;
+       # remove signatures 
+       $bunstr =~ s/Name:.*SHA1-Digest:.*//g;
+       @reqcomp = split /,/, $bunstr;
+       foreach $reqelement (@reqcomp) {
+               @reqelementfrmnt = split /;/, $reqelement;
+               $name = "";
+               $version = "";
+               $name = $reqelementfrmnt[0];
                $name =~ s/\"//g;
-                push @return, { NAME=>"$name", VERSION=>"$version"};
-        }
-
-        return @return;
+               # 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$/ );
+               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 {
-        my $ver = $_[0];
-        if ($ver eq "") { return "";}
-        if ($ver =~ m/(^[\[|\(])(.+)\ (.+)([\]|\)]$)/) {
-               # FIXME: The right rpm match of osgi version [1,2) seems to be <= 2
-               # but when you look at the requires >= look more permssive/correct?
-               ($1 eq "\[") ? return " >= $2" : return " > $2";
-        } else {
-                return " = $ver";
-        }
-        return $ver;
+       my $ver = $_[0];
+       if ( $ver eq "" ) { return ""; }
+       if ( $ver =~ m/(^[\[|\(])(.+)\ (.+)([\]|\)]$)/ ) {
+               if ( $1 eq "\[" ) {
+                       $ver = " >= $2";
+               } else {
+                       $ver = " > $2";
+               }
+       } else {
+               $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 {
-        my $version = $_[0];
-       # remove version qualifier.
-       $version =~ s/\.v.[0-9]*.*//g;
-       # We try to match RPM version, so remove last .0
-       $version =~ s/\.0$//g;
-       return $version;
+# this function put the max version on each bundles to be able to remove
+# duplicate deps with 'sort -u' command.
+sub prepareOSGiBundlesList {
+       foreach $bundle (@_) {
+               foreach $cmp (@_) {
+                       if ( $bundle->{NAME} eq $cmp->{NAME} ) {
+                               $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;
+}