/[pkg-kde]/scripts/autofixtll
ViewVC logotype

Contents of /scripts/autofixtll

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10291 - (show annotations) (download)
Sat Apr 26 22:44:41 2008 UTC (5 years ago) by modax-guest
File size: 15115 byte(s)
* Initial version of the script that is supposed to automatically fix the most of veeeerrryy boring linking failures with cleaned up KDELibs and Qt4 deps.
* Read `autofixtll -h` for more information.
* I made some changes that I've not tested yet so it might not work. I'll test very soon though.
1 #!/usr/bin/perl -w
2
3 =head1 NAME
4
5 B<autofixtll> - auto-correct CMake TARGET_LINK_LIBRARIES directive according to
6 the information provided by GNU ld linker S<I<'undefined references'>> errors.
7
8 =head1 SYNOPSIS
9
10 B<autofixtll> [B<--invoke-edit|-e>] [B<--build-dir|-b>=I<dir>] [B<--build-command|-c>=I<command>] [B<--patch-name|-p>=I<name>] [B<--do-backups>]
11
12 =head1 DESCRIPTION
13
14 B<autofixtll> is capable of correcting most linking failures caused by
15 S<'undefined references'> linker errors. The script should be executed from the
16 extracted debian source tree with all build dependences installed in the
17 environment. This script is a wrapper around S<`debian/rules build'> or
18 whatever command you specify with B<--build-command> option.
19
20 B<autofixtll> depends on B<quilt> to incrementally build up a patch of the
21 changes it does. The script assumes that quilt patches are located in
22 debian/patches. The default name of the patch is
23 S<I<"97_fix_target_link_libraries.diff">> (it can be changed with the
24 [B<--patch-name> option). This patch must have already pushed to the "quilt
25 top" when this script is executed.
26
27 First of all, the script loads dynamic symbol names of the libraries specified
28 in the @LIBS array. Then it starts building process and keeps monitoring it
29 while looking for the "undefined references" errors from the linker (only GNU
30 ld syntax is supported). Then it tries to match undefined symbol names with the
31 symbols loaded from the libraries in the @LIBS array. If matches are found, it
32 tries to update TARGET_LINK_LIBRARIES command in the respective CMakeLists.txt
33 file adding missing libraries. Finally, the build process is restarted.
34
35 This loop continues until build completes successfully or the error which
36 B<autofixtll> can't handle occurs. In latter case, a user will need to `quilt
37 edit' the respective file manually or add more libs to the @LIBS array. Then
38 the script can be restarted again (or use --invoke-edit option to invoke
39 I<quilt edit> and restart build for you automatically).
40
41 CMake verbose output must be enabled for B<autofixtll> to work reliably.
42
43 B<autofixtll> was written to help maintainer resolve build failures of KDE
44 applications which were introduced by the clean up of KDELibs recursive library
45 dependences.
46
47 =head1 OPTIONS
48
49 =over 4
50
51 =item B<-b> I<dir>, B<--build-dir>=I<dir>
52
53 Specify a custom build directory. It must be relative the current (source)
54 directory. Default is as returned by S<I<"obj-`dpkg-architecture
55 -qDEB_BUILD_GNU_TYPE`>">.
56
57 =item B<-c> I<command>, B<--build-command>=I<command>
58
59 The command which should be run to build the source. If the command is not
60 specific, `debian/rules build' will be executed from the B<source tree>.
61 However, if you specify the command, it will be from the B<build tree>.
62
63 =item B<-p>, B<--patch-name>=I<name>
64
65 The name of the quilt patch in which all changes made by the script will be
66 stored. Default is I<97_fix_target_link_libraries.diff>.
67
68 =item B<--do-backups>, B<--backup>
69
70 Whether to backup CMakeLists.txt as CMakeLists.txt.orig before auto-modifying
71 it.
72
73 =item B<--invoke-edit>, B<--edit>, B<-e>
74
75 Invoke `quilt edit' on the respective CMakeLists.txt when undefined references
76 cannot be automatically resolved and restart build process immediately when the
77 editor is closed.
78
79 =back
80
81 =head1 LICENSE
82
83 This program is free software: you can redistribute it and/or modify it under
84 the terms of the GNU General Public License as published by the Free Software
85 Foundation, either version 3 of the License, or (at your option) any later
86 version.
87
88 On B<Debian> systems, the complete text of the GNU GPL v3 can be found in the
89 file F</usr/share/common-licenses/GPL-3>
90
91 =head1 AUTHORS
92
93 Written by Modestas Vainius <modestas@vainius.eu>
94
95 =cut
96
97 use strict;
98 use Cwd qw(getcwd realpath);
99 use File::Spec;
100 use File::Copy;
101 use Getopt::Long;
102 use Pod::Usage;
103 use FileHandle;
104 use IPC::Open2;
105
106 #### Please add predefined libraries to load dynamic symbol from here.
107 # new Library(name, cmake_target, [path]). If path is not specified,
108 # it's path=/usr/lib/$name.so. Use '' quotes.
109 my @LIBS = (
110 new Library('QtDBus', '${QT_QTDBUS_LIBRARY}'),
111 new Library('QtNetwork', '${QT_QTNETWORK_LIBRARY}'),
112 new Library('QtXml', '${QT_QTXML_LIBRARY}'),
113 new Library('QtSvg', '${QT_QTSVG_LIBRARY}'),
114 new Library('X11', '${X11_X11_LIB}'),
115 new Library('z', '${ZLIB_LIBRARY}'),
116 );
117
118 #### Some defaults
119 my $QUILT = "QUILT_PATCHES=debian/patches quilt";
120 my $MSG_PREFIX = "--=--";
121
122 ############### Implementation ###############################
123
124 sub Library::new {
125 my ($cls, $name, $cmake_target, $path) = @_;
126 if (!defined $path) {
127 $path = "/usr/lib/lib$name.so";
128 }
129 return bless( { name => $name, path => $path, cmake_target => $cmake_target }, $cls);
130 }
131
132 sub Library::load {
133 my $self = shift;
134 my @symbols;
135 my @cpp_symbols;
136 if (-r $self->{path}) {
137 open(OBJDUMP, "objdump -T '$self->{path}' |") or die "Unable to run objdump";
138 while(<OBJDUMP>) {
139 # 0000000000021e80 g DF .text 00000000000000f9 Base _XSendClientPrefix
140 if (m/^[0-9a-f]+\s+g\s+DF\s+\.text\s+[0-9a-f]+\s+Base\s+(.*)$/) {
141 my $symbol = $1;
142 if ($symbol =~ m/^_Z/) {
143 # It is C++ symbol. Needs demangling.
144 push @cpp_symbols, $symbol;
145 } else {
146 # FIXME: use hashes?
147 push @symbols, $symbol;
148 }
149 }
150 }
151 close(OBJDUMP);
152 }
153 if (@cpp_symbols) {
154 open2(*OUT, *IN, "c++filt") or die "Unable to run c++filt";
155 for (@cpp_symbols) {
156 print IN "$_\n";
157 }
158 close(IN);
159 my $count = 0;
160 while(<OUT>) {
161 chomp;
162 # Don't care about everything after `('
163 s/\(.*$//;
164 s/^non-virtual thunk to\s+//;
165 push @symbols, $_;
166 $count++;
167 }
168 close(OUT);
169 print(STDERR "Lost a few C++ symbols (", (scalar(@cpp_symbols) - $count),
170 ") while demangling ", $self->to_string(), "\n") unless ($count == scalar(@cpp_symbols));
171 }
172 if (@symbols) {
173 $self->{symbols} = \@symbols;
174 return scalar(@symbols);
175 } else {
176 $self->{symbols} = [];
177 return 0;
178 }
179 }
180
181 sub Library::has_symbol {
182 my ($self, $symbol) = @_;
183
184 for my $sym (@{$self->{symbols}}) {
185 if ($symbol =~ m/^\Q$sym\E/) {
186 return 1;
187 }
188 }
189
190 return 0;
191 }
192
193 sub Library::to_string() {
194 my ($self) = @_;
195 return $self->{name} . " ( " . $self->{path} . " )";
196 }
197
198
199 sub determine_needed_libs {
200 my ($alllibs, $undefrefs) = @_;
201 my @_libs;
202 my @libs = ();
203
204 for my $ref (@$undefrefs) {
205 my $lib;
206 for my $lib (@$alllibs) {
207 if ($lib->has_symbol($ref)) {
208 push @_libs, $lib;
209 next;
210 }
211 }
212 }
213
214 # Kill dupes
215 my $prev = "";
216 for (sort { $a->{cmake_target} cmp $b->{cmake_target} } @_libs) {
217 if ($_ ne $prev) {
218 push @libs, $_;
219 $prev = $_;
220 }
221 }
222
223 return \@libs;
224 }
225
226 sub write_target_link_libs {
227 my ($dir, $target, $libs, $do_backups) = @_;
228 my $cmakelists = File::Spec->catfile($dir, "CMakeLists.txt");
229 my $strlibs = join(" ", map($_->{cmake_target}, @$libs));
230
231 if (-r $cmakelists) {
232 my @contents;
233 my $found = 0;
234
235 # Read and change
236 open(CMAKELISTS, "<$cmakelists");
237 while (<CMAKELISTS>) {
238 if (!$found && m/(target_link_libraries\s*\(\s*$target\s+)(.*?)(\s*\).*)?$/i) {
239 # Fix it
240 my $newline = $1;
241 my $end = $3;
242 $newline .= $2 if ($2);
243 $newline .= " " if ($newline !~ m/\s+$/);
244 $newline .= $strlibs;
245 $newline .= $end if ($end);
246 $newline .= "\n";
247 push @contents, $newline;
248 $found = $.;
249 } else {
250 push @contents, $_;
251 }
252 }
253 close(CMAKELISTS);
254
255 if (!$found) {
256 die "$MSG_PREFIX $cmakelists could not be corrected (needed '$strlibs' for target '$target'). Respective target_link_libraries() was not found $MSG_PREFIX";
257 } else {
258 # Write
259 system("$QUILT add '$cmakelists'");
260 open(CMAKELISTS, ">$cmakelists.tmp");
261 for (@contents) {
262 print CMAKELISTS $_;
263 }
264 close(CMAKELISTS);
265 if ($do_backups) {
266 File::Copy::move("$cmakelists", "$cmakelists.orig")
267 or die "Could not rename file '$cmakelists' -> '$cmakelists.org'";
268 }
269 File::Copy::move("$cmakelists.tmp", "$cmakelists")
270 or die "Could not rename file '$cmakelists.tmp' -> '$cmakelists'";
271 print "$MSG_PREFIX $cmakelists edited. Added libraries '$strlibs' for target $target\n";
272 }
273 } else {
274 die "$cmakelists for target $target could not be found. Something is wrong";
275 }
276 }
277
278 sub build_and_fix {
279 my ($sourcedir, $builddir, $buildcmd, $do_backups, $invoke_edit) = @_;
280
281 my $bdir;
282 my $btarget;
283 my $islderror = 0;
284 my @undefrefs;
285 my $link_cmd = 0;
286
287 if (defined $buildcmd) {
288 chdir $builddir;
289 open(MAKE, "$buildcmd 2>&1 |") or die "Unable to run `$buildcmd' in $builddir";
290 } else {
291 chdir $sourcedir;
292 open(MAKE, "debian/rules build 2>&1 |") or die "Unable to run `$buildcmd' in $builddir";
293 }
294
295 while (<MAKE>) {
296 # [ 39%] Building CXX object kwin/kcmkwin/kwindecoration/CMakeFiles
297 print $_;
298 chomp;
299 if ($link_cmd) {
300 #cd /buildd/kdebase-workspace/obj-x86_64-linux-gnu/kwin/kcmkwin/kwindecoration && /usr/bin/cmake -E cmake_link_script CMakeFiles/kcm_kwindecoration.dir/link.txt $MSG_PREFIXverbose=1
301 if (m#^cd (.*) && .*/cmake -E cmake_link_script CMakeFiles/(.*?)\.dir/#) {
302 $btarget = $2;
303 $bdir = File::Spec->abs2rel(Cwd::realpath($1), $builddir);
304 $link_cmd = 0;
305 } else {
306 die "Unrecognized link line";
307 }
308 } elsif (m/\[\s*\d+\%\] Building (.*) object (.*)/) {
309 $bdir = $2;
310 if ($bdir =~ m#CMakeFiles/(.*?)\.dir/#) {
311 $btarget = $1;
312 } else {
313 die "Could not extract target from $bdir";
314 }
315 $bdir =~ s#/CMakeFiles/.*##;
316 } elsif (m/^Linking (.*) (shared module|executable) /) {
317 $link_cmd = 1;
318 } elsif (m/undefined reference to `(.*)'$/) {
319 # undefined reference to `QDBusMessage::createSignal(QString const&, QString const&, QString const&)'
320 push @undefrefs, $1;
321 } elsif (m/collect\d+: ld returned \d+ exit status/) {
322 $islderror = 1;
323 }
324 }
325
326 close(MAKE);
327
328 chdir $sourcedir;
329
330 # Try to correct the error
331 if ($islderror && $bdir && $btarget && @undefrefs) {
332 my $libs = determine_needed_libs(\@LIBS, \@undefrefs);
333 if (@$libs) {
334 write_target_link_libs($bdir, $btarget, $libs, $do_backups);
335 return 0; # again
336 } else {
337 my $cmakelists = "$bdir/CMakeLists.txt";
338 print "$MSG_PREFIX Could not resolve linkage problem automatically. Undefined symbols have not been recognized\n";
339 print "$MSG_PREFIX Target: $btarget; CMakeLists.txt: $cmakelists", "\n";
340 my $quilt_cmd = "$QUILT edit $cmakelists";
341 if ($invoke_edit) {
342 print "$MSG_PREFIX Press any key to edit '$cmakelists' or ^C to cancel ...";
343 <>;
344 system($quilt_cmd);
345 } else {
346 print "$MSG_PREFIX You probably want to run the command to correct the problem yourself: \n",
347 "$MSG_PREFIX \$ $quilt_cmd\n";
348 return 2;
349 }
350 }
351 } else {
352 #print $islderror, ", ", $bdir, ", ", $btarget, ", ", @undefrefs, "\n";
353 print "$MSG_PREFIX Building completed successfully or unrecognized error\n";
354 return 1;
355 }
356 }
357
358 sub load_libraries {
359 my $LIBS = shift;
360
361 print "$MSG_PREFIX Reading dynamic symbol table of ", scalar(@$LIBS), " shared libraries... ", "\n";
362 my $count = 0;
363 for my $lib (@$LIBS) {
364 print "$MSG_PREFIX Loading ", $lib->to_string(), " ... ";
365 if ($lib->load()) {
366 print "success\n";
367 $count++;
368 } else {
369 print "failed (path is not readable or has no symbols)\n";
370 }
371 }
372 return $count;
373 }
374
375 sub check_environment {
376 my ($builddir, $patchname) = @_;
377
378 print "$MSG_PREFIX Script Version v$main::VERSION $MSG_PREFIX", "\n";
379
380 system("dh_testdir") == 0 or die "$MSG_PREFIX Please run this script from the debianized source tree $MSG_PREFIX\n";
381
382 my $toppatch = `$QUILT top`;
383 chomp $toppatch;
384 if (!defined $toppatch || $toppatch ne $patchname) {
385 die "$MSG_PREFIX Quilt top patch must be named '$patchname' when this script is run. Please either:\n" .
386 "$MSG_PREFIX \$ $QUILT push $patchname\n" .
387 "$MSG_PREFIX \$ $QUILT new $patchname\n";
388 }
389
390 if (! -d $builddir) {
391 die "Build directory '$builddir' does not exist";
392 }
393 }
394
395 sub get_gnu_build_type {
396 my $buildtype = `dpkg-architecture -qDEB_BUILD_GNU_TYPE`;
397 chomp $buildtype;
398 return $buildtype;
399 }
400
401 ############## Main loop ##############################
402
403 $main::VERSION = "0.2";
404
405 my $sourcedir = Cwd::getcwd();
406 my $builddir = "obj-" . get_gnu_build_type();
407 my $buildcmd = undef;
408 my $patchname = "97_fix_target_link_libraries.diff";
409 my $do_backups = 0;
410 my $show_help = 0;
411 my $invoke_edit = 0;
412
413 if (GetOptions(
414 "help|h|?" => \$show_help,
415 "build-dir|b=s" => \$builddir,
416 "build-command|c=s" => \$buildcmd,
417 "patch-name|p=s" => \$patchname,
418 "do-backups|backup!" => \$do_backups,
419 "invoke-edit|edit|e!" => \$invoke_edit,
420 )) {
421
422 pod2usage(-exitval => 1, -verbose => 2, -noperldoc => 1) if ($show_help);
423
424 $builddir = Cwd::realpath(File::Spec->catdir($sourcedir, $builddir));
425 check_environment($builddir, $patchname);
426
427 if (load_libraries(\@LIBS) == 0) {
428 die "Error: No dynamic symbols found in predefined libraries";
429 }
430
431 my $ret;
432 while (!($ret = build_and_fix($sourcedir, $builddir, $buildcmd, $do_backups, $invoke_edit))) {
433 print "$MSG_PREFIX Linkage problem fixed, rebuilding again\n";
434 }
435
436 if ($ret == 1) {
437 print "$MSG_PREFIX If build process is complete, you may want to run the following command to build/refresh the patch\n" .
438 "$MSG_PREFIX \$ $QUILT refresh --no-index\n";
439 if ($do_backups) {
440 print "$MSG_PREFIX Also run the following command to cleanup backup files:\n" .
441 "$MSG_PREFIX \$ find -name 'CMakeLists.txt.orig' -delete\n";
442 }
443 }
444
445 exit 0;
446 } else {
447 podusage(-exitval => 2, -verbose => 1);
448 }
449

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.5