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

Contents of /scripts/autofixtll

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10311 - (hide annotations) (download)
Mon Apr 28 01:00:13 2008 UTC (5 years ago) by modax-guest
File size: 17249 byte(s)
autofixtll v0.3

* Deals more carefully with target_link_libraries() enclosed in IFs/WHILEs/etc.
* A few small bugfixes

Recommended usage is:

autofixtll -e
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     =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 modax-guest 10311 # it's /usr/lib/$name.so. Use '' quotes to avoid escaping $.
109 modax-guest 10291 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 modax-guest 10311 sub IgnoreStack::new {
199     return bless( { stack => [] }, shift() );
200     }
201 modax-guest 10291
202 modax-guest 10311 sub IgnoreStack::process_line {
203     my ($self, $line) = @_;
204     my $stack = $self->{stack};
205    
206     if ($line =~ m/^\s*((end)?(foreach|function|if|macro|while))\s*[(]/i) {
207     my $isend = defined $2;
208     my $cmd = uc($3);
209    
210     if ($isend) {
211     if (@$stack) {
212     my $s = pop @$stack;
213     print STDERR "$MSG_PREFIX There is something wrong with IgnoreStack:\n",
214     "$MSG_PREFIX stack top ($s) does not match end command ($cmd)\n" if $s ne $cmd;
215     } else {
216     print STDERR "$MSG_PREFIX IgnoreStack is empty but got 'end$cmd'. A bug probably.\n";
217     }
218     } else {
219     push @$stack, $cmd;
220     }
221     return 1; # Processed
222     } else {
223     return 0;
224     }
225     }
226    
227     sub IgnoreStack::is_empty {
228     return scalar(@{shift()->{stack}}) == 0;
229     }
230    
231     sub IgnoreStack::dump_stack {
232     my $self = shift;
233     print "Ignore stack dump:\n";
234     for my $e (@{$self->{stack}}) {
235     print " $e\n";
236     }
237     }
238    
239 modax-guest 10291 sub determine_needed_libs {
240     my ($alllibs, $undefrefs) = @_;
241     my @_libs;
242     my @libs = ();
243    
244     for my $ref (@$undefrefs) {
245     my $lib;
246     for my $lib (@$alllibs) {
247     if ($lib->has_symbol($ref)) {
248     push @_libs, $lib;
249     next;
250     }
251     }
252     }
253    
254     # Kill dupes
255     my $prev = "";
256     for (sort { $a->{cmake_target} cmp $b->{cmake_target} } @_libs) {
257     if ($_ ne $prev) {
258     push @libs, $_;
259     $prev = $_;
260     }
261     }
262    
263     return \@libs;
264     }
265    
266     sub write_target_link_libs {
267     my ($dir, $target, $libs, $do_backups) = @_;
268     my $cmakelists = File::Spec->catfile($dir, "CMakeLists.txt");
269     my $strlibs = join(" ", map($_->{cmake_target}, @$libs));
270    
271     if (-r $cmakelists) {
272     my @contents;
273 modax-guest 10311 my @ignored;
274 modax-guest 10291 my $found = 0;
275 modax-guest 10311 # Ignore directive inside if/endif, while/endwhile etc. blocks
276     my $ignstack = new IgnoreStack;
277 modax-guest 10291
278     # Read and change
279     open(CMAKELISTS, "<$cmakelists");
280     while (<CMAKELISTS>) {
281 modax-guest 10311 if (!$found && !$ignstack->process_line($_) &&
282     m/^\s*(target_link_libraries\s*\(\s*$target\s+)(.*?)(\s*\).*)?$/i) {
283    
284 modax-guest 10291 # Fix it
285     my $newline = $1;
286     my $end = $3;
287     $newline .= $2 if ($2);
288     $newline .= " " if ($newline !~ m/\s+$/);
289     $newline .= $strlibs;
290     $newline .= $end if ($end);
291     $newline .= "\n";
292 modax-guest 10311
293     if ($ignstack->is_empty()) {
294     push @contents, $newline;
295     $found = $.;
296     } else {
297     # Save for later use
298     push @ignored, { found => $., newline => $newline };
299     push @contents, $_;
300     }
301 modax-guest 10291 } else {
302     push @contents, $_;
303     }
304     }
305     close(CMAKELISTS);
306    
307 modax-guest 10311 if (!$ignstack->is_empty()) {
308     $ignstack->dump_stack();
309     print STDERR "$cmakelists has been processed but ignore stack is not empty. Probably a bug!\n";
310     }
311    
312     if (!$found && @ignored == 1) {
313     # That's probably it as there were no other candidates. Replace
314     $found = ${ignored[0]}->{found};
315     my $newline = ${ignored[0]}->{newline};
316     $contents[$found-1] = $newline;
317     } else {
318     print "$MSG_PREFIX More (", scalar(@ignored), ") than 1 target_link_libraries() found in the IF/WHILE etc. blocks\n";
319     }
320    
321 modax-guest 10291 if (!$found) {
322     die "$MSG_PREFIX $cmakelists could not be corrected (needed '$strlibs' for target '$target'). Respective target_link_libraries() was not found $MSG_PREFIX";
323     } else {
324     # Write
325     system("$QUILT add '$cmakelists'");
326     open(CMAKELISTS, ">$cmakelists.tmp");
327     for (@contents) {
328     print CMAKELISTS $_;
329     }
330     close(CMAKELISTS);
331     if ($do_backups) {
332     File::Copy::move("$cmakelists", "$cmakelists.orig")
333     or die "Could not rename file '$cmakelists' -> '$cmakelists.org'";
334     }
335     File::Copy::move("$cmakelists.tmp", "$cmakelists")
336     or die "Could not rename file '$cmakelists.tmp' -> '$cmakelists'";
337     print "$MSG_PREFIX $cmakelists edited. Added libraries '$strlibs' for target $target\n";
338     }
339     } else {
340     die "$cmakelists for target $target could not be found. Something is wrong";
341     }
342     }
343    
344     sub build_and_fix {
345     my ($sourcedir, $builddir, $buildcmd, $do_backups, $invoke_edit) = @_;
346    
347     my $bdir;
348     my $btarget;
349     my $islderror = 0;
350     my @undefrefs;
351     my $link_cmd = 0;
352    
353     if (defined $buildcmd) {
354     chdir $builddir;
355     open(MAKE, "$buildcmd 2>&1 |") or die "Unable to run `$buildcmd' in $builddir";
356     } else {
357     chdir $sourcedir;
358     open(MAKE, "debian/rules build 2>&1 |") or die "Unable to run `$buildcmd' in $builddir";
359     }
360    
361     while (<MAKE>) {
362     # [ 39%] Building CXX object kwin/kcmkwin/kwindecoration/CMakeFiles
363     print $_;
364     chomp;
365     if ($link_cmd) {
366     #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
367     if (m#^cd (.*) && .*/cmake -E cmake_link_script CMakeFiles/(.*?)\.dir/#) {
368     $btarget = $2;
369     $bdir = File::Spec->abs2rel(Cwd::realpath($1), $builddir);
370     $link_cmd = 0;
371     } else {
372     die "Unrecognized link line";
373     }
374     } elsif (m/\[\s*\d+\%\] Building (.*) object (.*)/) {
375     $bdir = $2;
376     if ($bdir =~ m#CMakeFiles/(.*?)\.dir/#) {
377     $btarget = $1;
378     } else {
379     die "Could not extract target from $bdir";
380     }
381     $bdir =~ s#/CMakeFiles/.*##;
382 modax-guest 10311 } elsif (m/^Linking (.*) (shared (module|library)|executable) /) {
383 modax-guest 10291 $link_cmd = 1;
384     } elsif (m/undefined reference to `(.*)'$/) {
385     # undefined reference to `QDBusMessage::createSignal(QString const&, QString const&, QString const&)'
386     push @undefrefs, $1;
387     } elsif (m/collect\d+: ld returned \d+ exit status/) {
388     $islderror = 1;
389     }
390     }
391    
392     close(MAKE);
393    
394     chdir $sourcedir;
395    
396     # Try to correct the error
397 modax-guest 10311 # print $MSG_PREFIX, $islderror, $bdir, $btarget, @undefrefs;
398 modax-guest 10291 if ($islderror && $bdir && $btarget && @undefrefs) {
399     my $libs = determine_needed_libs(\@LIBS, \@undefrefs);
400     if (@$libs) {
401     write_target_link_libs($bdir, $btarget, $libs, $do_backups);
402     return 0; # again
403     } else {
404     my $cmakelists = "$bdir/CMakeLists.txt";
405     print "$MSG_PREFIX Could not resolve linkage problem automatically. Undefined symbols have not been recognized\n";
406     print "$MSG_PREFIX Target: $btarget; CMakeLists.txt: $cmakelists", "\n";
407     my $quilt_cmd = "$QUILT edit $cmakelists";
408     if ($invoke_edit) {
409     print "$MSG_PREFIX Press any key to edit '$cmakelists' or ^C to cancel ...";
410     <>;
411     system($quilt_cmd);
412     } else {
413     print "$MSG_PREFIX You probably want to run the command to correct the problem yourself: \n",
414     "$MSG_PREFIX \$ $quilt_cmd\n";
415     return 2;
416     }
417     }
418     } else {
419     #print $islderror, ", ", $bdir, ", ", $btarget, ", ", @undefrefs, "\n";
420     print "$MSG_PREFIX Building completed successfully or unrecognized error\n";
421     return 1;
422     }
423     }
424    
425     sub load_libraries {
426     my $LIBS = shift;
427    
428     print "$MSG_PREFIX Reading dynamic symbol table of ", scalar(@$LIBS), " shared libraries... ", "\n";
429     my $count = 0;
430     for my $lib (@$LIBS) {
431     print "$MSG_PREFIX Loading ", $lib->to_string(), " ... ";
432     if ($lib->load()) {
433     print "success\n";
434     $count++;
435     } else {
436     print "failed (path is not readable or has no symbols)\n";
437     }
438     }
439     return $count;
440     }
441    
442     sub check_environment {
443     my ($builddir, $patchname) = @_;
444    
445     print "$MSG_PREFIX Script Version v$main::VERSION $MSG_PREFIX", "\n";
446    
447     system("dh_testdir") == 0 or die "$MSG_PREFIX Please run this script from the debianized source tree $MSG_PREFIX\n";
448    
449     my $toppatch = `$QUILT top`;
450     chomp $toppatch;
451     if (!defined $toppatch || $toppatch ne $patchname) {
452     die "$MSG_PREFIX Quilt top patch must be named '$patchname' when this script is run. Please either:\n" .
453     "$MSG_PREFIX \$ $QUILT push $patchname\n" .
454     "$MSG_PREFIX \$ $QUILT new $patchname\n";
455     }
456     }
457    
458     sub get_gnu_build_type {
459     my $buildtype = `dpkg-architecture -qDEB_BUILD_GNU_TYPE`;
460     chomp $buildtype;
461     return $buildtype;
462     }
463    
464     ############## Main loop ##############################
465    
466 modax-guest 10311 $main::VERSION = "0.3";
467 modax-guest 10291
468     my $sourcedir = Cwd::getcwd();
469     my $builddir = "obj-" . get_gnu_build_type();
470     my $buildcmd = undef;
471     my $patchname = "97_fix_target_link_libraries.diff";
472     my $do_backups = 0;
473     my $show_help = 0;
474     my $invoke_edit = 0;
475    
476     if (GetOptions(
477     "help|h|?" => \$show_help,
478     "build-dir|b=s" => \$builddir,
479     "build-command|c=s" => \$buildcmd,
480     "patch-name|p=s" => \$patchname,
481     "do-backups|backup!" => \$do_backups,
482     "invoke-edit|edit|e!" => \$invoke_edit,
483     )) {
484    
485     pod2usage(-exitval => 1, -verbose => 2, -noperldoc => 1) if ($show_help);
486    
487     $builddir = Cwd::realpath(File::Spec->catdir($sourcedir, $builddir));
488     check_environment($builddir, $patchname);
489    
490     if (load_libraries(\@LIBS) == 0) {
491     die "Error: No dynamic symbols found in predefined libraries";
492     }
493    
494     my $ret;
495     while (!($ret = build_and_fix($sourcedir, $builddir, $buildcmd, $do_backups, $invoke_edit))) {
496     print "$MSG_PREFIX Linkage problem fixed, rebuilding again\n";
497     }
498    
499     if ($ret == 1) {
500     print "$MSG_PREFIX If build process is complete, you may want to run the following command to build/refresh the patch\n" .
501     "$MSG_PREFIX \$ $QUILT refresh --no-index\n";
502     if ($do_backups) {
503     print "$MSG_PREFIX Also run the following command to cleanup backup files:\n" .
504     "$MSG_PREFIX \$ find -name 'CMakeLists.txt.orig' -delete\n";
505     }
506     }
507    
508     exit 0;
509     } else {
510     podusage(-exitval => 2, -verbose => 1);
511     }
512    

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.5