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

Contents of /scripts/autofixtll

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10334 - (hide annotations) (download)
Mon Apr 28 15:42:27 2008 UTC (5 years ago) by modax-guest
File size: 18455 byte(s)
v0.4.1 - enhanced --invoke-edit.
1 modax-guest 10291 #!/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 modax-guest 10329 =item B<-i>, B<--exec-in-build-dir>
64    
65     Run build command in the build directory. Default is to run in the source
66     directory.
67    
68 modax-guest 10291 =item B<-p>, B<--patch-name>=I<name>
69    
70     The name of the quilt patch in which all changes made by the script will be
71     stored. Default is I<97_fix_target_link_libraries.diff>.
72    
73     =item B<--do-backups>, B<--backup>
74    
75     Whether to backup CMakeLists.txt as CMakeLists.txt.orig before auto-modifying
76     it.
77    
78     =item B<--invoke-edit>, B<--edit>, B<-e>
79    
80     Invoke `quilt edit' on the respective CMakeLists.txt when undefined references
81     cannot be automatically resolved and restart build process immediately when the
82     editor is closed.
83    
84     =back
85    
86     =head1 LICENSE
87    
88     This program is free software: you can redistribute it and/or modify it under
89     the terms of the GNU General Public License as published by the Free Software
90     Foundation, either version 3 of the License, or (at your option) any later
91     version.
92    
93     On B<Debian> systems, the complete text of the GNU GPL v3 can be found in the
94     file F</usr/share/common-licenses/GPL-3>
95    
96     =head1 AUTHORS
97    
98     Written by Modestas Vainius <modestas@vainius.eu>
99    
100     =cut
101    
102     use strict;
103     use Cwd qw(getcwd realpath);
104     use File::Spec;
105     use File::Copy;
106     use Getopt::Long;
107     use Pod::Usage;
108     use FileHandle;
109     use IPC::Open2;
110    
111     #### Please add predefined libraries to load dynamic symbol from here.
112     # new Library(name, cmake_target, [path]). If path is not specified,
113 modax-guest 10311 # it's /usr/lib/$name.so. Use '' quotes to avoid escaping $.
114 modax-guest 10291 my @LIBS = (
115     new Library('QtDBus', '${QT_QTDBUS_LIBRARY}'),
116     new Library('QtNetwork', '${QT_QTNETWORK_LIBRARY}'),
117     new Library('QtXml', '${QT_QTXML_LIBRARY}'),
118     new Library('QtSvg', '${QT_QTSVG_LIBRARY}'),
119 modax-guest 10329 new Library('QtGui', '${QT_QTGUI_LIBRARY}'),
120     # new Library('pthread', '${CMAKE_THREAD_LIBS_INIT}', '/lib/libpthread.so.0'),
121 modax-guest 10291 new Library('X11', '${X11_X11_LIB}'),
122     new Library('z', '${ZLIB_LIBRARY}'),
123 modax-guest 10329 new Library('solid', '${KDE4_SOLID_LIBS}'),
124 modax-guest 10291 );
125    
126     #### Some defaults
127     my $QUILT = "QUILT_PATCHES=debian/patches quilt";
128     my $MSG_PREFIX = "--=--";
129    
130     ############### Implementation ###############################
131    
132     sub Library::new {
133     my ($cls, $name, $cmake_target, $path) = @_;
134     if (!defined $path) {
135     $path = "/usr/lib/lib$name.so";
136     }
137     return bless( { name => $name, path => $path, cmake_target => $cmake_target }, $cls);
138     }
139    
140     sub Library::load {
141     my $self = shift;
142     my @symbols;
143 modax-guest 10329 my %symbhash;
144 modax-guest 10291 my @cpp_symbols;
145     if (-r $self->{path}) {
146     open(OBJDUMP, "objdump -T '$self->{path}' |") or die "Unable to run objdump";
147     while(<OBJDUMP>) {
148     # 0000000000021e80 g DF .text 00000000000000f9 Base _XSendClientPrefix
149     if (m/^[0-9a-f]+\s+g\s+DF\s+\.text\s+[0-9a-f]+\s+Base\s+(.*)$/) {
150     my $symbol = $1;
151     if ($symbol =~ m/^_Z/) {
152     # It is C++ symbol. Needs demangling.
153     push @cpp_symbols, $symbol;
154     } else {
155     push @symbols, $symbol;
156 modax-guest 10329 $symbhash{$symbol} = 1;
157 modax-guest 10291 }
158     }
159     }
160     close(OBJDUMP);
161     }
162     if (@cpp_symbols) {
163     open2(*OUT, *IN, "c++filt") or die "Unable to run c++filt";
164 modax-guest 10329 my $count = 0;
165 modax-guest 10291 for (@cpp_symbols) {
166     print IN "$_\n";
167 modax-guest 10329 $_ = <OUT>;
168 modax-guest 10291 chomp;
169 modax-guest 10329 # Don't care about anything after `('
170 modax-guest 10291 s/\(.*$//;
171     s/^non-virtual thunk to\s+//;
172     push @symbols, $_;
173 modax-guest 10329 $symbhash{$_} = 1;
174 modax-guest 10291 $count++;
175     }
176 modax-guest 10329 close(IN);
177 modax-guest 10291 close(OUT);
178     print(STDERR "Lost a few C++ symbols (", (scalar(@cpp_symbols) - $count),
179     ") while demangling ", $self->to_string(), "\n") unless ($count == scalar(@cpp_symbols));
180     }
181     if (@symbols) {
182     $self->{symbols} = \@symbols;
183 modax-guest 10329 $self->{symbhash} = \%symbhash;
184 modax-guest 10291 return scalar(@symbols);
185     } else {
186     $self->{symbols} = [];
187 modax-guest 10329 $self->{symbhash} = {};
188 modax-guest 10291 return 0;
189     }
190     }
191    
192     sub Library::has_symbol {
193     my ($self, $symbol) = @_;
194    
195     for my $sym (@{$self->{symbols}}) {
196     if ($symbol =~ m/^\Q$sym\E/) {
197     return 1;
198     }
199     }
200     return 0;
201     }
202    
203 modax-guest 10329 sub Library::has_symbol_fast {
204     my ($self, $symbol) = @_;
205    
206     # Don't care about anything after `('
207     $symbol =~ s/\(.*$//;
208    
209     return (exists $self->{symbhash}{$symbol});
210     }
211    
212 modax-guest 10291 sub Library::to_string() {
213     my ($self) = @_;
214     return $self->{name} . " ( " . $self->{path} . " )";
215     }
216    
217 modax-guest 10311 sub IgnoreStack::new {
218     return bless( { stack => [] }, shift() );
219     }
220 modax-guest 10291
221 modax-guest 10311 sub IgnoreStack::process_line {
222     my ($self, $line) = @_;
223     my $stack = $self->{stack};
224    
225     if ($line =~ m/^\s*((end)?(foreach|function|if|macro|while))\s*[(]/i) {
226     my $isend = defined $2;
227     my $cmd = uc($3);
228    
229     if ($isend) {
230     if (@$stack) {
231     my $s = pop @$stack;
232     print STDERR "$MSG_PREFIX There is something wrong with IgnoreStack:\n",
233     "$MSG_PREFIX stack top ($s) does not match end command ($cmd)\n" if $s ne $cmd;
234     } else {
235     print STDERR "$MSG_PREFIX IgnoreStack is empty but got 'end$cmd'. A bug probably.\n";
236     }
237     } else {
238     push @$stack, $cmd;
239     }
240     return 1; # Processed
241     } else {
242     return 0;
243     }
244     }
245    
246     sub IgnoreStack::is_empty {
247     return scalar(@{shift()->{stack}}) == 0;
248     }
249    
250     sub IgnoreStack::dump_stack {
251     my $self = shift;
252     print "Ignore stack dump:\n";
253     for my $e (@{$self->{stack}}) {
254     print " $e\n";
255     }
256     }
257    
258 modax-guest 10291 sub determine_needed_libs {
259     my ($alllibs, $undefrefs) = @_;
260     my @_libs;
261     my @libs = ();
262 modax-guest 10329 my @notfound;
263 modax-guest 10291
264 modax-guest 10329 # Try fast search first
265 modax-guest 10291 for my $ref (@$undefrefs) {
266     my $lib;
267     for my $lib (@$alllibs) {
268 modax-guest 10329 if ($lib->has_symbol_fast($ref)) {
269     push @_libs, $lib;
270     next;
271     }
272     }
273     push @notfound, $ref;
274     }
275    
276     # Then try slow one
277     for my $ref (@notfound) {
278     my $lib;
279     for my $lib (@$alllibs) {
280 modax-guest 10291 if ($lib->has_symbol($ref)) {
281     push @_libs, $lib;
282     next;
283     }
284     }
285     }
286    
287     # Kill dupes
288     my $prev = "";
289     for (sort { $a->{cmake_target} cmp $b->{cmake_target} } @_libs) {
290     if ($_ ne $prev) {
291     push @libs, $_;
292     $prev = $_;
293     }
294     }
295    
296     return \@libs;
297     }
298    
299     sub write_target_link_libs {
300     my ($dir, $target, $libs, $do_backups) = @_;
301     my $cmakelists = File::Spec->catfile($dir, "CMakeLists.txt");
302     my $strlibs = join(" ", map($_->{cmake_target}, @$libs));
303    
304     if (-r $cmakelists) {
305     my @contents;
306 modax-guest 10311 my @ignored;
307 modax-guest 10291 my $found = 0;
308 modax-guest 10311 # Ignore directive inside if/endif, while/endwhile etc. blocks
309     my $ignstack = new IgnoreStack;
310 modax-guest 10291
311     # Read and change
312     open(CMAKELISTS, "<$cmakelists");
313     while (<CMAKELISTS>) {
314 modax-guest 10311 if (!$found && !$ignstack->process_line($_) &&
315     m/^\s*(target_link_libraries\s*\(\s*$target\s+)(.*?)(\s*\).*)?$/i) {
316    
317 modax-guest 10291 # Fix it
318     my $newline = $1;
319     my $end = $3;
320     $newline .= $2 if ($2);
321     $newline .= " " if ($newline !~ m/\s+$/);
322     $newline .= $strlibs;
323     $newline .= $end if ($end);
324     $newline .= "\n";
325 modax-guest 10311
326     if ($ignstack->is_empty()) {
327     push @contents, $newline;
328     $found = $.;
329     } else {
330     # Save for later use
331     push @ignored, { found => $., newline => $newline };
332     push @contents, $_;
333     }
334 modax-guest 10291 } else {
335     push @contents, $_;
336     }
337     }
338     close(CMAKELISTS);
339    
340 modax-guest 10311 if (!$ignstack->is_empty()) {
341     $ignstack->dump_stack();
342     print STDERR "$cmakelists has been processed but ignore stack is not empty. Probably a bug!\n";
343     }
344    
345     if (!$found && @ignored == 1) {
346     # That's probably it as there were no other candidates. Replace
347     $found = ${ignored[0]}->{found};
348     my $newline = ${ignored[0]}->{newline};
349     $contents[$found-1] = $newline;
350 modax-guest 10329 } elsif (!$found && @ignored > 1) {
351 modax-guest 10311 print "$MSG_PREFIX More (", scalar(@ignored), ") than 1 target_link_libraries() found in the IF/WHILE etc. blocks\n";
352     }
353    
354 modax-guest 10291 if (!$found) {
355 modax-guest 10334 print "$MSG_PREFIX $cmakelists could not be corrected (needed '$strlibs' for target '$target'). Respective target_link_libraries() was not found $MSG_PREFIX\n";
356     return 0;
357 modax-guest 10291 } else {
358     # Write
359     system("$QUILT add '$cmakelists'");
360     open(CMAKELISTS, ">$cmakelists.tmp");
361     for (@contents) {
362     print CMAKELISTS $_;
363     }
364     close(CMAKELISTS);
365     if ($do_backups) {
366     File::Copy::move("$cmakelists", "$cmakelists.orig")
367     or die "Could not rename file '$cmakelists' -> '$cmakelists.org'";
368     }
369     File::Copy::move("$cmakelists.tmp", "$cmakelists")
370     or die "Could not rename file '$cmakelists.tmp' -> '$cmakelists'";
371     print "$MSG_PREFIX $cmakelists edited. Added libraries '$strlibs' for target $target\n";
372 modax-guest 10334 return 1;
373 modax-guest 10291 }
374     } else {
375     die "$cmakelists for target $target could not be found. Something is wrong";
376     }
377     }
378    
379 modax-guest 10334 sub invoke_edit {
380     my ($cmakelists, $do_invoke) = @_;
381     my $quilt_cmd = "$QUILT edit '$cmakelists'";
382    
383     if ($do_invoke) {
384     print "$MSG_PREFIX Press ENTER to edit '$cmakelists' or ^C to cancel ...";
385     <>;
386     return (system($quilt_cmd) == 0) ? 0 : 2;
387     } else {
388     print "$MSG_PREFIX You probably want to run the command to correct the problem yourself: \n",
389     "$MSG_PREFIX \$ $quilt_cmd\n";
390     return 2;
391     }
392     }
393    
394 modax-guest 10291 sub build_and_fix {
395 modax-guest 10329 my ($sourcedir, $builddir, $buildcmd, $exec_in_build_dir, $do_backups, $invoke_edit) = @_;
396 modax-guest 10291
397     my $bdir;
398     my $btarget;
399     my $islderror = 0;
400     my @undefrefs;
401     my $link_cmd = 0;
402    
403 modax-guest 10329 if ($exec_in_build_dir) {
404 modax-guest 10291 chdir $builddir;
405     } else {
406     chdir $sourcedir;
407     }
408    
409 modax-guest 10329 open(MAKE, "$buildcmd 2>&1 |") or die "Unable to run `$buildcmd' in $builddir";
410    
411 modax-guest 10291 while (<MAKE>) {
412     # [ 39%] Building CXX object kwin/kcmkwin/kwindecoration/CMakeFiles
413     print $_;
414     chomp;
415     if ($link_cmd) {
416     #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
417     if (m#^cd (.*) && .*/cmake -E cmake_link_script CMakeFiles/(.*?)\.dir/#) {
418     $btarget = $2;
419     $bdir = File::Spec->abs2rel(Cwd::realpath($1), $builddir);
420     $link_cmd = 0;
421     } else {
422     die "Unrecognized link line";
423     }
424     } elsif (m/\[\s*\d+\%\] Building (.*) object (.*)/) {
425     $bdir = $2;
426     if ($bdir =~ m#CMakeFiles/(.*?)\.dir/#) {
427     $btarget = $1;
428     } else {
429     die "Could not extract target from $bdir";
430     }
431     $bdir =~ s#/CMakeFiles/.*##;
432 modax-guest 10311 } elsif (m/^Linking (.*) (shared (module|library)|executable) /) {
433 modax-guest 10291 $link_cmd = 1;
434     } elsif (m/undefined reference to `(.*)'$/) {
435     # undefined reference to `QDBusMessage::createSignal(QString const&, QString const&, QString const&)'
436     push @undefrefs, $1;
437     } elsif (m/collect\d+: ld returned \d+ exit status/) {
438     $islderror = 1;
439     }
440     }
441    
442     close(MAKE);
443    
444     chdir $sourcedir;
445    
446     # Try to correct the error
447 modax-guest 10311 # print $MSG_PREFIX, $islderror, $bdir, $btarget, @undefrefs;
448 modax-guest 10291 if ($islderror && $bdir && $btarget && @undefrefs) {
449     my $libs = determine_needed_libs(\@LIBS, \@undefrefs);
450     if (@$libs) {
451 modax-guest 10334 if (write_target_link_libs($bdir, $btarget, $libs, $do_backups)) {
452     return 0; # again
453     } else {
454     return invoke_edit("$bdir/CMakeLists.txt", $invoke_edit);
455     }
456 modax-guest 10291 } else {
457     my $cmakelists = "$bdir/CMakeLists.txt";
458     print "$MSG_PREFIX Could not resolve linkage problem automatically. Undefined symbols have not been recognized\n";
459     print "$MSG_PREFIX Target: $btarget; CMakeLists.txt: $cmakelists", "\n";
460 modax-guest 10334 return invoke_edit($cmakelists, $invoke_edit);
461 modax-guest 10291 }
462     } else {
463     #print $islderror, ", ", $bdir, ", ", $btarget, ", ", @undefrefs, "\n";
464     print "$MSG_PREFIX Building completed successfully or unrecognized error\n";
465     return 1;
466     }
467     }
468    
469     sub load_libraries {
470     my $LIBS = shift;
471    
472     print "$MSG_PREFIX Reading dynamic symbol table of ", scalar(@$LIBS), " shared libraries... ", "\n";
473     my $count = 0;
474     for my $lib (@$LIBS) {
475     print "$MSG_PREFIX Loading ", $lib->to_string(), " ... ";
476     if ($lib->load()) {
477     print "success\n";
478     $count++;
479     } else {
480     print "failed (path is not readable or has no symbols)\n";
481     }
482     }
483     return $count;
484     }
485    
486     sub check_environment {
487     my ($builddir, $patchname) = @_;
488    
489     print "$MSG_PREFIX Script Version v$main::VERSION $MSG_PREFIX", "\n";
490    
491     system("dh_testdir") == 0 or die "$MSG_PREFIX Please run this script from the debianized source tree $MSG_PREFIX\n";
492    
493     my $toppatch = `$QUILT top`;
494     chomp $toppatch;
495     if (!defined $toppatch || $toppatch ne $patchname) {
496     die "$MSG_PREFIX Quilt top patch must be named '$patchname' when this script is run. Please either:\n" .
497     "$MSG_PREFIX \$ $QUILT push $patchname\n" .
498     "$MSG_PREFIX \$ $QUILT new $patchname\n";
499     }
500     }
501    
502     sub get_gnu_build_type {
503     my $buildtype = `dpkg-architecture -qDEB_BUILD_GNU_TYPE`;
504     chomp $buildtype;
505     return $buildtype;
506     }
507    
508     ############## Main loop ##############################
509    
510 modax-guest 10334 $main::VERSION = "0.4.1";
511 modax-guest 10291
512     my $sourcedir = Cwd::getcwd();
513     my $builddir = "obj-" . get_gnu_build_type();
514 modax-guest 10329 my $exec_in_build_dir = 0;
515     my $buildcmd = "debian/rules build";
516 modax-guest 10291 my $patchname = "97_fix_target_link_libraries.diff";
517     my $do_backups = 0;
518     my $show_help = 0;
519     my $invoke_edit = 0;
520    
521     if (GetOptions(
522     "help|h|?" => \$show_help,
523     "build-dir|b=s" => \$builddir,
524     "build-command|c=s" => \$buildcmd,
525 modax-guest 10329 "invoke-edit|edit|e!" => \$invoke_edit,
526     "exec-in-build-dir|i!" => \$exec_in_build_dir,
527 modax-guest 10291 "patch-name|p=s" => \$patchname,
528     "do-backups|backup!" => \$do_backups,
529     )) {
530    
531     pod2usage(-exitval => 1, -verbose => 2, -noperldoc => 1) if ($show_help);
532    
533     $builddir = Cwd::realpath(File::Spec->catdir($sourcedir, $builddir));
534     check_environment($builddir, $patchname);
535    
536     if (load_libraries(\@LIBS) == 0) {
537     die "Error: No dynamic symbols found in predefined libraries";
538     }
539    
540     my $ret;
541 modax-guest 10329 while (!($ret = build_and_fix($sourcedir, $builddir, $buildcmd, $exec_in_build_dir, $do_backups, $invoke_edit))) {
542 modax-guest 10291 print "$MSG_PREFIX Linkage problem fixed, rebuilding again\n";
543     }
544    
545     if ($ret == 1) {
546     print "$MSG_PREFIX If build process is complete, you may want to run the following command to build/refresh the patch\n" .
547     "$MSG_PREFIX \$ $QUILT refresh --no-index\n";
548     if ($do_backups) {
549     print "$MSG_PREFIX Also run the following command to cleanup backup files:\n" .
550     "$MSG_PREFIX \$ find -name 'CMakeLists.txt.orig' -delete\n";
551     }
552     }
553    
554     exit 0;
555     } else {
556     podusage(-exitval => 2, -verbose => 1);
557     }
558    

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.5