| 1 | #! /usr/bin/perl -w |
|---|
| 2 | |
|---|
| 3 | =head1 NAME |
|---|
| 4 | |
|---|
| 5 | dh_girepository - compute dependencies for GObject introspection packages |
|---|
| 6 | |
|---|
| 7 | =cut |
|---|
| 8 | |
|---|
| 9 | use strict; |
|---|
| 10 | use File::Find; |
|---|
| 11 | use Debian::Debhelper::Dh_Lib; |
|---|
| 12 | |
|---|
| 13 | =head1 SYNOPSIS |
|---|
| 14 | |
|---|
| 15 | B<dh_girepository> [I<debhelper options>] [-lI<directory>] [-pI<directory>] [-XI<item>] [I<private [...]>] |
|---|
| 16 | |
|---|
| 17 | =head1 DESCRIPTION |
|---|
| 18 | |
|---|
| 19 | dh_girepository is a debhelper program to compute dependencies for packages |
|---|
| 20 | shipping GObject introspection data. |
|---|
| 21 | |
|---|
| 22 | The dependencies are generated in the ${gir:Depends} substitution variable. |
|---|
| 23 | |
|---|
| 24 | =head1 OPTIONS |
|---|
| 25 | |
|---|
| 26 | =over 4 |
|---|
| 27 | |
|---|
| 28 | =item B<-l>I<directory> |
|---|
| 29 | |
|---|
| 30 | Specify a directory (or a colon-separated list of directories) where to look |
|---|
| 31 | for the .gir XML files that were used to generate the .typelib files that |
|---|
| 32 | are scanned. This option is only necessary if those files are not shipped in |
|---|
| 33 | another, architecture-dependent package. |
|---|
| 34 | |
|---|
| 35 | =item B<-p>I<directory> |
|---|
| 36 | |
|---|
| 37 | Specify a directory (or a colon-separated list of directories) where to look |
|---|
| 38 | for the dependencies. This is useful when a dependency ships the .typelib |
|---|
| 39 | in a private directory. |
|---|
| 40 | |
|---|
| 41 | =item B<-X>I<item> |
|---|
| 42 | |
|---|
| 43 | Exclude files that contain I<item> anywhere in their filename from being |
|---|
| 44 | analyzed. |
|---|
| 45 | |
|---|
| 46 | =item I<private [...]> |
|---|
| 47 | List of directories where to look for typelibs and the corresponding .gir |
|---|
| 48 | files. Useful when the package installs its typelibs in a private |
|---|
| 49 | directory, such as /usr/lib/<package>. Library dependencies are also looked |
|---|
| 50 | there, in case your typelib depends on a library that you ship on a private |
|---|
| 51 | directory. |
|---|
| 52 | |
|---|
| 53 | =back |
|---|
| 54 | |
|---|
| 55 | =head1 CONFORMS TO |
|---|
| 56 | |
|---|
| 57 | GObject introspection mini policy as of 2010-12-07. |
|---|
| 58 | |
|---|
| 59 | =cut |
|---|
| 60 | |
|---|
| 61 | # Initialisation code |
|---|
| 62 | init(options => { |
|---|
| 63 | "l=s", => \$dh{L_PARAMS}, |
|---|
| 64 | "p=s", => \$dh{P_PARAMS}, |
|---|
| 65 | }); |
|---|
| 66 | my @paths_first = (); |
|---|
| 67 | my @privdirs = (); |
|---|
| 68 | if ($dh{L_PARAMS}) { |
|---|
| 69 | push @paths_first, split /:/, $dh{L_PARAMS}; |
|---|
| 70 | } |
|---|
| 71 | if ($dh{P_PARAMS}) { |
|---|
| 72 | push @privdirs, split /:/, $dh{P_PARAMS}; |
|---|
| 73 | } |
|---|
| 74 | isnative($dh{MAINPACKAGE}); # Necessary to have $dh{VERSION} |
|---|
| 75 | my $bin_version = $dh{VERSION}; |
|---|
| 76 | my @archpackages = getpackages("arch"); |
|---|
| 77 | |
|---|
| 78 | my $typelib_path = "/usr/lib/girepository-1.0"; |
|---|
| 79 | my @typelibdirs = (@ARGV, $typelib_path); |
|---|
| 80 | my $gir_path = "/usr/share/gir-1.0"; |
|---|
| 81 | my @girdirs = (@ARGV, $gir_path); |
|---|
| 82 | my $arch_triplet = `dpkg-architecture -qDEB_BUILD_GNU_TYPE`; |
|---|
| 83 | chomp $arch_triplet; |
|---|
| 84 | my @privlibdirs = (@ARGV); |
|---|
| 85 | my @libdirs = ("/lib/$arch_triplet", "/lib", "/usr/lib/$arch_triplet", "/usr/lib", @privlibdirs); |
|---|
| 86 | my $format; |
|---|
| 87 | |
|---|
| 88 | # Get Build-Depends in an array |
|---|
| 89 | my @bdeps; |
|---|
| 90 | my $cur = 0; |
|---|
| 91 | open (my $control, "<", "debian/control") or error ("Cannot open debian/control"); |
|---|
| 92 | while (<$control>) { |
|---|
| 93 | chomp; |
|---|
| 94 | s/\s+$//; |
|---|
| 95 | if ($cur) { |
|---|
| 96 | if (/^\s+(.*)$/) { |
|---|
| 97 | push @bdeps, split ",",$1; |
|---|
| 98 | if ($1 !~ /,$/) { |
|---|
| 99 | $cur = 0; |
|---|
| 100 | } |
|---|
| 101 | } else { |
|---|
| 102 | $cur = 0; |
|---|
| 103 | } |
|---|
| 104 | } |
|---|
| 105 | if (/^Build-Depends:\s*(.*)$/) { |
|---|
| 106 | push @bdeps, split ",",$1; |
|---|
| 107 | if ($1 =~ /,$/) { |
|---|
| 108 | $cur = 1; |
|---|
| 109 | } else { |
|---|
| 110 | $cur = 0; |
|---|
| 111 | } |
|---|
| 112 | } |
|---|
| 113 | } |
|---|
| 114 | close $control; |
|---|
| 115 | |
|---|
| 116 | |
|---|
| 117 | # We canât parse .typelib files, so we need the corresponding .gir |
|---|
| 118 | # somewhere in the same source package (or with -l). |
|---|
| 119 | |
|---|
| 120 | sub find_gir { |
|---|
| 121 | my $req = shift; |
|---|
| 122 | $req =~ s/\.typelib$//; |
|---|
| 123 | my $f; |
|---|
| 124 | foreach my $path (@paths_first) { |
|---|
| 125 | $f = "$path/$req.gir"; |
|---|
| 126 | if (-f $f) { |
|---|
| 127 | verbose_print ("Found $req.gir in $path"); |
|---|
| 128 | return $f; |
|---|
| 129 | } |
|---|
| 130 | } |
|---|
| 131 | foreach my $otherpkg (@archpackages) { |
|---|
| 132 | foreach my $girdir (@girdirs) { |
|---|
| 133 | $f = tmpdir($otherpkg)."$girdir/$req.gir"; |
|---|
| 134 | if (-f $f) { |
|---|
| 135 | verbose_print ("Found $req.gir in $otherpkg package"); |
|---|
| 136 | return $f; |
|---|
| 137 | } |
|---|
| 138 | } |
|---|
| 139 | } |
|---|
| 140 | error("Could not find gir file for $req.typelib"); |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | |
|---|
| 144 | # Function used for dependencies on other .typelib files |
|---|
| 145 | |
|---|
| 146 | sub require_typelib { |
|---|
| 147 | my $req = shift; |
|---|
| 148 | my $package = shift; |
|---|
| 149 | my $fullpath = "$typelib_path/$req"; |
|---|
| 150 | |
|---|
| 151 | verbose_print ("Dependency: $req"); |
|---|
| 152 | foreach my $girdir (@girdirs) { |
|---|
| 153 | if (-f tmpdir($package)."$girdir/$req") { |
|---|
| 154 | verbose_print(" found in the same package"); |
|---|
| 155 | return; |
|---|
| 156 | } |
|---|
| 157 | } |
|---|
| 158 | foreach my $otherpkg (@archpackages) { |
|---|
| 159 | if (-f tmpdir($otherpkg)."$fullpath") { |
|---|
| 160 | verbose_print (" found in $otherpkg"); |
|---|
| 161 | error("Dependency on $otherpkg with a different format than $format") unless $otherpkg =~ /^gir$format/; |
|---|
| 162 | addsubstvar ($package, "gir:Depends", $otherpkg, "= $bin_version"); |
|---|
| 163 | return; |
|---|
| 164 | } |
|---|
| 165 | } |
|---|
| 166 | foreach my $privpath (@privdirs) { |
|---|
| 167 | if (-f "$privpath/$req") { |
|---|
| 168 | verbose_print (" found in $privpath"); |
|---|
| 169 | $fullpath = "$privpath/$req"; |
|---|
| 170 | last; |
|---|
| 171 | } |
|---|
| 172 | } |
|---|
| 173 | error("Could not find $req dependency") unless -f "$fullpath"; |
|---|
| 174 | my @output = (split ':', `dpkg -S $fullpath 2>/dev/null`); |
|---|
| 175 | error("$fullpath does not belong to any package") unless @output; |
|---|
| 176 | my $deppkg = $output[0]; |
|---|
| 177 | error("Dependency on $deppkg with a different format than $format") unless $deppkg =~ /^gir$format/; |
|---|
| 178 | # Look for version information in build-dependencies |
|---|
| 179 | my $found = 0; |
|---|
| 180 | foreach my $bdep (@bdeps) { |
|---|
| 181 | if ($bdep =~ /^\s*([a-z0-9\._\-\+]+)\s*\((.*)\)/) { |
|---|
| 182 | if ($1 eq $deppkg) { |
|---|
| 183 | addsubstvar ($package, "gir:Depends", $1, $2); |
|---|
| 184 | $found = 1; |
|---|
| 185 | } |
|---|
| 186 | } |
|---|
| 187 | } |
|---|
| 188 | if (! $found) { |
|---|
| 189 | addsubstvar ($package, "gir:Depends", $deppkg); |
|---|
| 190 | } |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | |
|---|
| 194 | sub find_library_in_package { |
|---|
| 195 | my $req = shift; |
|---|
| 196 | my $package = shift; |
|---|
| 197 | my $tmp = ""; |
|---|
| 198 | if ($package) { |
|---|
| 199 | $tmp = tmpdir ($package); |
|---|
| 200 | } |
|---|
| 201 | my @loclibdirs = grep -d, map "$tmp$_", @libdirs; |
|---|
| 202 | foreach my $libdir (@loclibdirs) { |
|---|
| 203 | if (-f "$libdir/$req" or -l "$libdir/$req") { |
|---|
| 204 | return "$libdir/$req"; |
|---|
| 205 | } |
|---|
| 206 | } |
|---|
| 207 | } |
|---|
| 208 | |
|---|
| 209 | sub find_library { |
|---|
| 210 | my $req = shift; |
|---|
| 211 | my $package = shift; |
|---|
| 212 | |
|---|
| 213 | my $file = find_library_in_package ($req, $package); |
|---|
| 214 | if ($file) { |
|---|
| 215 | verbose_print (" found in the same package"); |
|---|
| 216 | } else { |
|---|
| 217 | foreach my $otherpkg (@archpackages) { |
|---|
| 218 | $file = find_library_in_package ($req, $otherpkg); |
|---|
| 219 | if ($file) { |
|---|
| 220 | verbose_print (" found in $otherpkg"); |
|---|
| 221 | last; |
|---|
| 222 | } |
|---|
| 223 | } |
|---|
| 224 | } |
|---|
| 225 | if (!$file) { |
|---|
| 226 | $file = find_library_in_package ($req); |
|---|
| 227 | if ($file) { |
|---|
| 228 | verbose_print (" found on filesystem"); |
|---|
| 229 | } else { |
|---|
| 230 | error ("Could not find library $req"); |
|---|
| 231 | } |
|---|
| 232 | } |
|---|
| 233 | |
|---|
| 234 | if (-l $file and not -f $file) { |
|---|
| 235 | # We have a symbolic link that points to another package |
|---|
| 236 | verbose_print (" ... it's a symlink ..."); |
|---|
| 237 | return find_library (readlink ($file), $package); |
|---|
| 238 | } |
|---|
| 239 | return $file; |
|---|
| 240 | } |
|---|
| 241 | |
|---|
| 242 | foreach my $package (@{$dh{DOPACKAGES}}) { |
|---|
| 243 | my $tmp = tmpdir($package); |
|---|
| 244 | my $ext = pkgext($package); |
|---|
| 245 | my @bin_files = (); |
|---|
| 246 | my @c_files = (); |
|---|
| 247 | my @typelib_deps = (); |
|---|
| 248 | foreach my $dir (@typelibdirs) { |
|---|
| 249 | my $typelibdir = "$tmp$dir"; |
|---|
| 250 | next unless -d $typelibdir; |
|---|
| 251 | opendir(DIRHANDLE, $typelibdir); |
|---|
| 252 | while (my $typelib = readdir(DIRHANDLE)) { |
|---|
| 253 | next unless $typelib =~ /\.typelib$/; |
|---|
| 254 | next if excludefile ($typelib); |
|---|
| 255 | my $girfile = find_gir ($typelib); |
|---|
| 256 | error("Unable to open $girfile") unless open (my $f, "<", $girfile); |
|---|
| 257 | verbose_print ("$girfile..."); |
|---|
| 258 | my @libraries = (); |
|---|
| 259 | my @symbols = (); |
|---|
| 260 | my $infunction = 0; |
|---|
| 261 | while (<$f>) { |
|---|
| 262 | # "Parse" the XML file |
|---|
| 263 | chomp; |
|---|
| 264 | if (/<repository.+version="(.*?)"/) { |
|---|
| 265 | # gir format version |
|---|
| 266 | $format="$1"; |
|---|
| 267 | } elsif (/<include\s+name="(.*?)"\s+version="(.*?)"\/>/) { |
|---|
| 268 | # Dependency on another typelib file |
|---|
| 269 | my $deptypelib="$1-$2.typelib"; |
|---|
| 270 | verbose_print (" Dependency: $deptypelib"); |
|---|
| 271 | if (! grep { $_ eq $deptypelib } @typelib_deps) { |
|---|
| 272 | push @typelib_deps, $deptypelib; |
|---|
| 273 | } |
|---|
| 274 | } elsif (/shared-library="(.*?)"/) { |
|---|
| 275 | # Dependency on a shared library |
|---|
| 276 | foreach my $shlibname (split ",", $1) { |
|---|
| 277 | if ($shlibname !~ /\.so/) { |
|---|
| 278 | $shlibname = "lib$shlibname.so" |
|---|
| 279 | } |
|---|
| 280 | verbose_print (" Library: $shlibname"); |
|---|
| 281 | push @libraries, find_library ($shlibname, $package); |
|---|
| 282 | } |
|---|
| 283 | } elsif (/<(method|constructor|function)\s.*c:identifier="(.*?)"/) { |
|---|
| 284 | push @symbols, $2; |
|---|
| 285 | } elsif (/<(method|constructor|function)/) { |
|---|
| 286 | $infunction = 1; |
|---|
| 287 | } elsif ($infunction and /c:identifier="(.*?)"/) { |
|---|
| 288 | push @symbols, $1; |
|---|
| 289 | } |
|---|
| 290 | if (/>$/) { |
|---|
| 291 | $infunction = 0; |
|---|
| 292 | } |
|---|
| 293 | } |
|---|
| 294 | close $f; |
|---|
| 295 | error("Unable to determine gir format") unless $format; |
|---|
| 296 | error("Package name $package doesn't match gir format $format") |
|---|
| 297 | unless $package =~ /^gir$format/ |
|---|
| 298 | or not $typelibdir =~ /usr\/lib\/girepository/; |
|---|
| 299 | verbose_print(sprintf(" %d symbols found", $#symbols+1)); |
|---|
| 300 | if (@libraries or @symbols) { |
|---|
| 301 | my $c_file = "$typelibdir/$typelib.c"; |
|---|
| 302 | my $bin_file = "$typelibdir/$typelib.so"; |
|---|
| 303 | verbose_print (" writing $c_file"); |
|---|
| 304 | if (!$dh{NO_ACT}){ |
|---|
| 305 | error("Unable to open $girfile") unless open (F, ">", $c_file); |
|---|
| 306 | print F "void gir_dummy_function () {\n"; |
|---|
| 307 | foreach my $symbol (@symbols) { |
|---|
| 308 | print F "$symbol ();\n"; |
|---|
| 309 | } |
|---|
| 310 | print F "}"; |
|---|
| 311 | close F; |
|---|
| 312 | } |
|---|
| 313 | push @c_files, $c_file; |
|---|
| 314 | |
|---|
| 315 | # Build a dummy binary using all referenced symbols and libraries |
|---|
| 316 | # We use -shared so that gcc doesnât try to resolve references |
|---|
| 317 | verbose_print (" building $bin_file"); |
|---|
| 318 | doit (("gcc", "-shared", "-fPIC", "-o", $bin_file, $c_file, @libraries)); |
|---|
| 319 | push @bin_files, $bin_file; |
|---|
| 320 | } |
|---|
| 321 | } |
|---|
| 322 | } |
|---|
| 323 | if (@bin_files) { |
|---|
| 324 | # dpkg-shlibdeps expects this directory to exist |
|---|
| 325 | if (! -d "$tmp/DEBIAN") { |
|---|
| 326 | doit("install","-o",0,"-g",0,"-d","$tmp/DEBIAN"); |
|---|
| 327 | } |
|---|
| 328 | |
|---|
| 329 | # Let dpkg-shlibdeps generate the corresponding dependencies |
|---|
| 330 | # It must run first since otherwise it overwrites the variable |
|---|
| 331 | complex_doit ("LD_LIBRARY_PATH=" . join (':', @privlibdirs) . " dpkg-shlibdeps -pgir -Tdebian/${ext}substvars -xlibc6 -xlibc0 @bin_files"); |
|---|
| 332 | } |
|---|
| 333 | doit (("rm", "-f", @c_files, @bin_files)); |
|---|
| 334 | |
|---|
| 335 | # Generate dependencies on other .typelib files |
|---|
| 336 | foreach my $dep (@typelib_deps) { |
|---|
| 337 | require_typelib ($dep, $package); |
|---|
| 338 | } |
|---|
| 339 | } |
|---|
| 340 | |
|---|
| 341 | =head1 SEE ALSO |
|---|
| 342 | |
|---|
| 343 | L<debhelper(7)> |
|---|
| 344 | |
|---|
| 345 | This program is a part of gobject-introspection but is made to work with |
|---|
| 346 | debhelper. |
|---|
| 347 | |
|---|
| 348 | =head1 AUTHOR |
|---|
| 349 | |
|---|
| 350 | Josselin Mouette <joss@debian.org> |
|---|
| 351 | |
|---|
| 352 | =cut |
|---|