3 # osgideps.pl -- Analyze dependencies of OSGi bundles.
5 # Kyu Lee (initial idea)
6 # Alphonse Van Assche <alcapcom@fedoraproject.org> (current maintainer)
8 # $Id: osgideps.pl,v 1.0 2009/06/08 12:12:12 mej Exp $
11 use File::Temp qw/ tempdir /;
15 $MANIFEST_NAME = "META-INF/MANIFEST.MF";
18 my ( $show_provides, $show_requires, $show_system_bundles, $debug );
19 my $result = GetOptions(
20 "provides" => \$show_provides,
21 "requires" => \$show_requires,
22 "system" => \$show_system_bundles,
25 exit(1) if ( not $result );
27 # run selected function
30 getProvides(@allfiles);
33 getRequires(@allfiles);
35 if ($show_system_bundles) {
36 getSystemBundles(@allfiles);
40 # this function print provides of OSGi aware files
43 my $queue = Thread::Queue->new;
45 $queue->enqueue($file);
49 push @workers, threads->create('getProvidesWorker');
50 push @workers, threads->create('getProvidesWorker');
51 push @workers, threads->create('getProvidesWorker');
52 push @workers, threads->create('getProvidesWorker');
54 map { $_->join } @workers;
56 sub getProvidesWorker {
57 while ( my $file = $queue->dequeue_nb ) {
59 # we don't follow symlinks for provides
60 next if ( -f $file && -r $file && -l $file );
61 $file =~ s/[^[:print:]]//g;
62 if ( $file =~ m/$MANIFEST_NAME$/ || $file =~ m/\.jar$/ ) {
63 if ( $file =~ m/\.jar$/ ) {
64 if ( `zipinfo -1 $file 2> /dev/null | grep -e \^$MANIFEST_NAME` eq "$MANIFEST_NAME\n" ) {
65 # extract MANIFEST.MF file from jar to temporary directory
66 $tmpdir = tempdir( CLEANUP => 1 );
67 `unzip -d $tmpdir -qqo $file $MANIFEST_NAME`;
68 open( MANIFEST, "$tmpdir/$MANIFEST_NAME" );
71 open( MANIFEST, "$file" );
75 # parse Bundle-SymbolicName, Bundle-Version and Export-Package attributes
77 # get rid of non-print chars (some manifest files contain weird chars)
79 if ( m/(^(Bundle-SymbolicName): )(.*)$/ ) {
80 $bundleName = "$3" . "\n";
82 if ( m/^[[:upper:]][[:alpha:]]+-[[:upper:]][[:alpha:]]+: .*/ ) {
84 seek MANIFEST, $len * -1, 1;
89 $bundleName =~ s/\s+//g;
90 $bundleName =~ s/;.*//g;
92 if ( m/(^Bundle-Version: )(.*)/ ) {
95 if ( m/(^(Export-Package): )(.*)$/ ) {
96 my $bunlist = "$3" . "\n";
98 if ( m/^[[:upper:]][[:alpha:]]+-[[:upper:]][[:alpha:]]+: .*/ ) {
100 seek MANIFEST, $len * -1, 1;
105 push @bundlelist, parsePkgString($bunlist, $file);
109 # skip this jar if no bundle name exists
110 if ( !$bundleName eq "" ) {
111 if ( !$version eq "" ) {
112 $version = parseVersion($version);
113 push @bundlelist, { FILE => "$file", NAME => "$bundleName", VERSION => "$version" };
115 push @bundlelist, { FILE => "$file", NAME => "$bundleName", VERSION => "" };
121 if ( !$debug ) { @bundlelist = prepareOSGiBundlesList(@bundlelist); }
123 for $bundle (@bundlelist) {
125 $list .= "osgi(" . $bundle->{NAME} . ")" . $bundle->{VERSION} . "\n";
127 $list .= $bundle->{FILE} . " osgi(" . $bundle->{NAME} . ")" . $bundle->{VERSION} . "\n";
134 # this function print requires of OSGi aware files
137 my $queue = Thread::Queue->new;
139 $queue->enqueue($file);
143 push @workers, threads->create('getRequiresWorker');
144 push @workers, threads->create('getRequiresWorker');
145 push @workers, threads->create('getRequiresWorker');
146 push @workers, threads->create('getRequiresWorker');
148 map { $_->join } @workers;
150 sub getRequiresWorker {
151 while ( my $file = $queue->dequeue_nb ) {
152 next if ( -f $file && -r $file );
153 $file =~ s/[^[:print:]]//g;
154 if ( $file =~ m/$MANIFEST_NAME$/ || $file =~ m/\.jar$/ ) {
155 # we explicitly requires symlinked jars
156 # _that_reside_outside_the_package_
159 $lnksrc = `readlink -qen $file`;
160 foreach $exfile ( @allfiles ) {
161 $exfile =~ s/[^[:print:]]//g;
162 if ( $lnksrc =~ m/$exfile$/ ) {
167 print "$lnksrc\n" if (!$exist);
171 if ( $file =~ m/\.jar$/ ) {
172 if ( `zipinfo -1 $file 2> /dev/null | grep -e \^$MANIFEST_NAME` eq "$MANIFEST_NAME\n" ) {
173 # extract MANIFEST.MF file from jar to temporary directory
174 $tmpdir = tempdir( CLEANUP => 1 );
175 `unzip -d $tmpdir -qqo $file $MANIFEST_NAME`;
176 open( MANIFEST, "$tmpdir/$MANIFEST_NAME" );
179 open( MANIFEST, "$file" );
182 if ( m/(^(Require-Bundle|Import-Package): )(.*)$/ ) {
183 my $bunlist = "$3" . "\n";
185 if (m/^[[:upper:]][[:alpha:]]+-[[:upper:]][[:alpha:]]+: .*/ ) {
187 seek MANIFEST, $len * -1, 1;
192 push @bundlelist, parsePkgString($bunlist, $file);
194 # we also explicitly require symlinked jars define by
195 # Bundle-ClassPath attribut
196 if ( m/(^(Bundle-ClassPath): )(.*)$/ ) {
197 $bunclp = "$3" . "\n";
199 if ( m/^[[:upper:]][[:alpha:]]+-[[:upper:]][[:alpha:]]+: .*/ ) {
201 seek MANIFEST, $len * -1, 1;
208 $bunclp =~ s/[^[:print:]]//g;
209 $dir = `dirname $file`;
211 @jars = split /,/, $bunclp;
212 for $jarfile (@jars) {
213 $jarfile = "$dir\/\.\.\/$jarfile";
214 $jarfile = readlink $jarfile;
215 if ( !$jarfile eq "" ) {
216 print "$jarfile" . "\n";
224 if ( !$debug ) { @bundlelist = prepareOSGiBundlesList(@bundlelist); }
226 for $bundle (@bundlelist) {
227 # replace '=' by '>=' because qualifiers are set on provides
228 # but not on requires.
229 $bundle->{VERSION} =~ s/\ =/\ >=/g;
231 $list .= "osgi(" . $bundle->{NAME} . ")" . $bundle->{VERSION} . "\n";
233 $list .= $bundle->{FILE} . " osgi(" . $bundle->{NAME} . ")" . $bundle->{VERSION} . "\n";
240 # this function print system bundles of OSGi profile files.
241 sub getSystemBundles {
243 if ( -f $file && -r $file ) {
244 print "'$file' file not found or cannot be read!";
247 open( PROFILE, "$file" );
249 if ( $file =~ m/\.profile$/ ) {
250 if (m/(^(org\.osgi\.framework\.system\.packages)[=|\ ]+)(.*)$/) {
251 $syspkgs = "$3" . "\n";
255 seek MANIFEST, $len * -1, 1;
260 $syspkgs =~ s/\s+//g;
262 @bundles = split /,/, $syspkgs;
263 foreach $bundle (@bundles) {
264 print "osgi(" . $bundle . ")\n";
279 $bunstr =~ s/[^[:print:]]//g;
280 $bunstr =~ s/("[[:alnum:]|\-|\_|\.|\(|\)|\[|\]]+)(,)([[:alnum:]|\-|\_|\.|\(|\)|\[|\]]+")/$1 $3/g;
281 # remove uses bundle from Export-Package attribute
282 $bunstr =~ s/uses:="[[:alnum:]|\-|\_|\.|\(|\)|\[|\]|,]+"//g;
283 # remove optional dependencies
284 $bunstr =~ s/,.*;resolution:=optional//g;
286 $bunstr =~ s/;x-friends:="[[:alnum:]|\-|\_|\.|\(|\)|\[|\]|,]+"//g;
288 $bunstr =~ s/Name:.*SHA1-Digest:.*//g;
289 @reqcomp = split /,/, $bunstr;
290 foreach $reqelement (@reqcomp) {
291 @reqelementfrmnt = split /;/, $reqelement;
294 $name = $reqelementfrmnt[0];
296 # ignoring OSGi 'system.bundle'
297 next if ( $name =~ m/^system\.bundle$/ );
298 for $i ( 1 .. $#reqelementfrmnt ) {
299 if ( $reqelementfrmnt[$i] =~ m/(^(bundle-|)version=")(.*)(")/ ) {
304 $version = parseVersion($version);
305 push @return, { FILE => "$file", NAME => "$name", VERSION => "$version" };
312 if ( $ver eq "" ) { return ""; }
313 if ( $ver =~ m/(^[\[|\(])(.+)\ (.+)([\]|\)]$)/ ) {
322 # we always return a full OSGi version to be able to match 1.0
323 # and 1.0.0 as equal in RPM.
324 ( $major, $minor, $micro, $qualifier ) = split( '\.', $ver );
325 if ( !defined($minor) || !$minor ) {
328 if ( !defined($micro) || !$micro ) {
331 if ( !defined($qualifier) || !$qualifier ) {
334 $qualifier = "." . $qualifier;
336 $ver = $major . "." . $minor . "." . $micro . $qualifier;
340 # this function put the max version on each bundles to be able to remove
341 # duplicate deps with 'sort -u' command.
342 sub prepareOSGiBundlesList {
343 foreach $bundle (@_) {
345 if ( $bundle->{NAME} eq $cmp->{NAME} ) {
346 $result = compareVersion( $bundle->{VERSION}, $cmp->{VERSION} );
348 $bundle->{VERSION} = $cmp->{VERSION};
356 # this function returns a negative integer, zero, or a positive integer if
357 # $ver1 is less than, equal to, or greater than $ver2.
359 # REMEMBER: we mimic org.osgi.framework.Version#compareTo method but
360 # *at this time* we don't take care of the qualifier part of the version.
365 $ver1 = "0.0.0" if ( $ver1 eq "" );
366 $ver2 = "0.0.0" if ( $ver2 eq "" );
368 $ver1 =~ m/([0-9]+)(\.)([0-9]+)(\.)([0-9]+)/;
373 $ver2 =~ m/([0-9]+)(\.)([0-9]+)(\.)([0-9]+)/;
378 $result = $major1 - $major2;
379 return $result if ( $result != 0 );
381 $result = $minor1 - $minor2;
382 return $result if ( $result != 0 );
384 $result = $micro1 - $micro2;
385 return $result if ( $result != 0 );