commit 7417cd283a3641ee62b282ae36c5d2d7df349d04 Author: Panu Matilainen Date: Thu Jun 18 13:06:42 2009 +0300 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 diff --git a/scripts/osgideps.pl b/scripts/osgideps.pl index 7b02016..971dade 100644 --- a/scripts/osgideps.pl +++ b/scripts/osgideps.pl @@ -1,203 +1,357 @@ #!/usr/bin/perl - +# +# osgideps.pl -- Analyze dependencies of OSGi bundles. +# +# Kyu Lee +# Alphonse Van Assche +# +# $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 = ; - 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() { - # 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 () { + # get rid of non-print chars (some manifest files contain weird chars) + s/[^[:print]]//g; + if ( m/(^(Bundle-SymbolicName): )(.*)$/ ) { + $bundleName = "$3" . "\n"; + while () { + 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 () { + 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() { - 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 () { + if ( m/(^(Require-Bundle|Import-Package): )(.*)$/ ) { + my $bunlist = "$3" . "\n"; + while () { + 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 () { + 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() { - if (m/(^(Require-Bundle|Import-Package): )(.*)$/) { - my $bunlist = "$3"."\n"; - while() { - 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 () { + if ( $file =~ m/\.profile$/ ) { + if (m/(^(org\.osgi\.framework\.system\.packages)[=|\ ]+)(.*)$/) { + $syspkgs = "$3" . "\n"; + while () { + 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; +}