]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - configure
Remove .*.d dependency garbage, and use a dedicated build directory
[user/henk/code/inspircd.git] / configure
1 #!/usr/bin/perl
2 ###################################################
3 # InspIRCd Configuration Script
4 #
5 # Copyright 2002-2009 The InspIRCd Development Team
6 #  http://wiki.inspircd.org/Credits
7 #
8 # Licensed under GPL, please see the COPYING file
9 # for more information
10 #
11 # $Id$
12 #
13 ###################################################
14
15 BEGIN {
16         require 5.8.0;
17 }
18
19 use strict;
20 use warnings FATAL => qw(all);
21
22 use Data::Dumper;
23 BEGIN {
24         $Data::Dumper::Sortkeys = 1;
25         $Data::Dumper::Useqq = 1;
26 };
27
28 use File::Copy ();
29 use Socket;
30 use Cwd;
31 use Getopt::Long;
32
33 # Utility functions for our buildsystem
34 use make::utilities;
35 use make::configure;
36 use make::gnutlscert;
37 use make::opensslcert;
38
39 ###############################################################################################
40 #
41 #                                 NON-EDITABLE VARIABLES
42 #
43 ###############################################################################################
44
45 our ($opt_use_gnutls, $opt_rebuild, $opt_use_openssl, $opt_nointeractive, $opt_ports,
46     $opt_epoll, $opt_kqueue, $opt_noports, $opt_noepoll, $opt_nokqueue,
47     $opt_noipv6, $opt_maxbuf, $opt_disable_debug, $opt_freebsd_port);
48
49 our ($opt_cc, $opt_base_dir, $opt_config_dir, $opt_module_dir, $opt_binary_dir,
50     $opt_library_dir);
51
52 sub list_extras ();
53
54 sub enable_extras (@);
55
56 sub disable_extras (@);
57
58 my @opt_enableextras;
59 my @opt_disableextras;
60
61 GetOptions (
62         'enable-gnutls' => \$opt_use_gnutls,
63         'rebuild' => \$opt_rebuild,
64         'enable-openssl' => \$opt_use_openssl,
65         'disable-interactive' => \$opt_nointeractive,
66         'enable-ports' => \$opt_ports,
67         'enable-epoll' => \$opt_epoll,
68         'enable-kqueue' => \$opt_kqueue,
69         'disable-ports' => \$opt_noports,
70         'disable-epoll' => \$opt_noepoll,
71         'disable-kqueue' => \$opt_nokqueue,
72         'disable-ipv6' => \$opt_noipv6,
73         'with-cc=s' => \$opt_cc,
74         'with-maxbuf=i' => \$opt_maxbuf,
75         'enable-freebsd-ports-openssl' => \$opt_freebsd_port,
76         'prefix=s' => \$opt_base_dir,
77         'config-dir=s' => \$opt_config_dir,
78         'module-dir=s' => \$opt_module_dir,
79         'binary-dir=s' => \$opt_binary_dir,
80         'library-dir=s' => \$opt_library_dir,
81         'disable-debuginfo' => sub { $opt_disable_debug = 1 },
82         'help'  => sub { showhelp(); },
83         'update' => sub { update(); },
84         'svnupdate' => sub { svnupdate(); },
85         'clean' => sub { clean(); },
86         'list-extras' => sub { list_extras; exit 0; }, # This, --enable-extras, and --disable-extras are for non-interactive managing.
87         'enable-extras=s@' => \@opt_enableextras, # ^
88         'disable-extras=s@' => \@opt_disableextras, # ^
89         'generate-openssl-cert' => sub { make_openssl_cert(); exit(0); },
90         'generate-gnutls-cert' => sub { make_gnutls_cert(); exit(0); }
91 );
92
93 if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
94         @opt_enableextras = split /,/, join(',', @opt_enableextras);
95         @opt_disableextras = split /,/, join(',', @opt_disableextras);
96         enable_extras(@opt_enableextras);
97         disable_extras(@opt_disableextras);
98         list_extras;
99         print "Remember: YOU are responsible for making sure any libraries needed have been installed!\n";
100         exit 0;
101 }
102
103 our $non_interactive = (
104         (defined $opt_library_dir) ||
105         (defined $opt_base_dir) ||
106         (defined $opt_config_dir) ||
107         (defined $opt_module_dir) ||
108         (defined $opt_base_dir) ||
109         (defined $opt_binary_dir) ||
110         (defined $opt_nointeractive) ||
111         (defined $opt_cc) ||
112         (defined $opt_noipv6) ||
113         (defined $opt_kqueue) ||
114         (defined $opt_epoll) ||
115         (defined $opt_ports) ||
116         (defined $opt_use_openssl) ||
117         (defined $opt_nokqueue) ||
118         (defined $opt_noepoll) ||
119         (defined $opt_noports) ||
120         (defined $opt_maxbuf) ||
121         (defined $opt_use_gnutls) ||
122         (defined $opt_freebsd_port)
123 );
124 our $interactive = !$non_interactive;
125
126 chomp(our $topdir = getcwd());
127 our $this = resolve_directory($topdir);                                         # PWD, Regardless.
128 our @modlist = ();                                                                      # Declare for Module List..
129 our %config = ();                                                                       # Initiate Configuration Hash..
130 $config{ME}              = resolve_directory($topdir);                          # Present Working Directory
131
132 $config{BASE_DIR}          = $config{ME};
133
134 if (defined $opt_base_dir)
135 {
136         $config{BASE_DIR} = $opt_base_dir;
137 }
138
139 $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");        # Configuration Directory
140 $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
141 $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");         # Binary Directory
142
143 if (defined $opt_config_dir)
144 {
145         $config{CONFIG_DIR} = $opt_config_dir;
146 }
147 if (defined $opt_module_dir)
148 {
149         $config{MODULE_DIR} = $opt_module_dir;
150 }
151 if (defined $opt_binary_dir)
152 {
153         $config{BINARY_DIR} = $opt_binary_dir;
154 }
155 chomp($config{HAS_GNUTLS}   = `pkg-config --modversion gnutls 2>/dev/null | cut -c 1,2,3`); # GNUTLS Version.
156
157 if (defined $opt_freebsd_port)
158 {
159         chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`);
160         chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);
161         $config{USE_FREEBSD_BASE_SSL} = "n";
162 }
163 else
164 {
165         if ($^O eq "freebsd")
166         {
167                 # default: use base ssl
168                 chomp($config{HAS_OPENSSL} = `openssl version | cut -d ' ' -f 2`);                      # OpenSSL version, freebsd specific
169                 chomp($config{HAS_OPENSSL_PORT}  = `pkg-config --modversion openssl 2>/dev/null`);      # Port version, may be different
170         }
171         else
172         {
173                 chomp($config{HAS_OPENSSL}  = `pkg-config --modversion openssl 2>/dev/null`);           # Openssl version, others
174                 $config{HAS_OPENSSL_PORT} = "";
175         }
176 }
177
178 chomp(our $gnutls_ver = $config{HAS_GNUTLS});
179 chomp(our $openssl_ver = $config{HAS_OPENSSL});
180 $config{USE_GNUTLS}         = "n";
181 if (defined $opt_use_gnutls)
182 {
183         $config{USE_GNUTLS} = "y";                                      # Use gnutls.
184 }
185 $config{USE_OPENSSL}    = "n";                                          # Use openssl.
186 if (defined $opt_use_openssl)
187 {
188         $config{USE_OPENSSL} = "y";
189 }
190
191 # no, let's not change these.
192 $config{OPTIMITEMP}      = "0";                                         # Default Optimisation Value
193 if (!defined $opt_disable_debug)
194 {
195         $config{OPTIMISATI}      = "-g1";                               # Optimisation Flag
196 }
197 else
198 {
199         $config{OPTIMISATI}      = "-O2";                               # DEBUGGING OFF!
200 }
201
202 $config{HAS_STRLCPY}    = "false";                                      # strlcpy Check.
203 $config{HAS_STDINT}      = "false";                                     # stdint.h check
204 $config{USE_KQUEUE}      = "y";                                         # kqueue enabled
205 if (defined $opt_kqueue)
206 {
207         $config{USE_KQUEUE} = "y";
208 }
209 if (defined $opt_nokqueue)
210 {
211         $config{USE_KQUEUE} = "n";
212 }
213 $config{USE_POLL}     = "y";                                    # poll enabled
214 $config{USE_EPOLL}        = "y";                                        # epoll enabled
215 if (defined $opt_epoll)
216 {
217         $config{USE_EPOLL} = "y";
218 }
219 if (defined $opt_noepoll)
220 {
221         $config{USE_EPOLL} = "n";
222 }
223 $config{USE_PORTS}        = "y";                                        # epoll enabled
224 if (defined $opt_ports)
225 {
226         $config{USE_PORTS} = "y";
227 }
228 if (defined $opt_noports)
229 {
230         $config{USE_PORTS} = "n";
231 }
232 $config{IPV6}          = "y";                                   # IPv6 support
233 if (defined $opt_noipv6)
234 {
235         $config{IPV6} = "n";
236 }
237 chomp($config{GCCVER}       = `g++ -dumpversion | cut -c 1`);           # Major GCC Version
238 chomp($config{GCCMINOR}     = `g++ -dumpversion | cut -c 3`);
239 $config{_SOMAXCONN} = SOMAXCONN;                                        # Max connections in accept queue
240 $config{OSNAME}             = $^O;                                      # Operating System Name
241 $config{IS_DARWIN}        = "NO";                                       # Is OSX?
242 $config{STARTSCRIPT}      = "inspircd";                 # start script?
243 $config{DESTINATION}      = "BASE";                             # Is target path.
244 $config{EXTRA_DIR}        = "";                                         # Is empty.
245 if ($config{OSNAME} =~ /darwin/i)
246 {
247         $config{IS_DARWIN} = "YES";
248         $config{STARTSCRIPT}      = "org.inspircd.plist";               # start script for OSX.
249         $config{DESTINATION}      = "LAUNCHDPATH";                              # Is OSX target.
250         $config{EXTRA_DIR}          = " launchd_dir";                           # Is OSX specific path.
251 }
252 $config{CC}                 = "g++";                                            # C++ compiler
253 if (defined $opt_cc)
254 {
255         $config{CC} = $opt_cc;
256 }
257 our $exec = $config{CC} . " -dumpversion | cut -c 1";
258 chomp($config{GCCVER}           = `$exec`);                             # Major GCC Version
259 $exec = $config{CC} . " -dumpversion | cut -c 3";
260 chomp($config{GCCMINOR}         = `$exec`);
261 $config{MAXBUF}                 = "512";                                # Max buffer size
262
263 if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)([a-z])?(\-[a-z][0-9])?$/) {
264         $config{HAS_OPENSSL} = $1;
265 } else {
266         $config{HAS_OPENSSL} = "";
267 }
268
269 if (($config{GCCVER} eq "") || ($config{GCCMINOR} eq "")) {
270         print $config{CC} . " was not found! You require g++ (the GNU C++ compiler, part of GCC) to build InspIRCd!\n";
271         exit;
272 }
273
274 # Get and Set some important vars..
275 getmodules();
276
277 sub clean
278 {
279         unlink(".config.cache");
280 }
281
282 our ($has_epoll, $has_ports, $has_kqueue) = (0, 0, 0);
283
284 sub update
285 {
286         eval {
287                 chomp($topdir = getcwd());
288                 $this = resolve_directory($topdir);                                          # PWD, Regardless.
289                 getmodules();
290                 # Does the cache file exist?
291                 if (!getcache()) {
292                         # No, No it doesn't.. *BASH*
293                         print "You have not run ./configure before. Please do this before trying to run the update script.\n";
294                         exit 0;
295                 } else {
296                         # We've Loaded the cache file and all our variables..
297                         print "Updating files...\n";
298                         if (defined($opt_disable_debug) && $opt_disable_debug == 1)
299                         {
300                                 print "Disabling debug information (-g).\n";
301                                 $config{OPTIMISATI} = "";
302                         }
303                         $has_epoll = $config{HAS_EPOLL};
304                         $has_ports = $config{HAS_PORTS};
305                         $has_kqueue = $config{HAS_KQUEUE};
306                         writefiles(1);
307                         makecache();
308                         print "Complete.\n";
309                         exit;
310                 }
311         };
312         if ($@)
313         {
314                 print "Configure update failed: $@\n";
315         }
316         exit;
317 }
318
319 sub svnupdate
320 {
321         my $fail = 0;
322         open(FH,"<.svn/entries") or $fail = 1;
323         if ($fail) {
324                 print "This is not an SVN copy of InspIRCd.\n";
325                 exit 1;
326         }
327         else
328         {
329                 close(FH);
330         }
331         open my $fd, "-|", "svn update";
332         my $configurechanged = 0; # Needs ./configure -update
333         my $rootincchanged = 0;
334         my @conflicted = ();
335         while (defined(my $line = <$fd>))
336         {
337                 my ($action, $file);
338                 print $line;
339                 $line =~ m/^([ADUCG])\s+(.*)$/ or next;
340                 ($action, $file) = ($1, $2);
341                 if ($action eq "C")
342                 {
343                         push @conflicted, $file;
344                         if ($file eq "configure")
345                         {
346                                 $configurechanged = 1;
347                         }
348                         elsif ($file =~ m/^\..*\.inc$/)
349                         {
350                                 $rootincchanged = 1;
351                         }
352                 }
353                 elsif ($action eq "U" || $action eq "G")
354                 {
355                         if ($file eq "configure")
356                         {
357                                 $configurechanged = 1;
358                         }
359                         elsif ($file =~ m/^\..*\.inc$/)
360                         {
361                                 $rootincchanged = 1;
362                         }
363                 }
364         }
365         unless (close $fd) # close() waits for exit and returns false if the command failed
366         {
367                 if ($! == 0)
368                 {
369                         print STDERR "Problem updating from SVN, please check above for errors\n";
370                 }
371                 else
372                 {
373                         print STDERR "Failed to run SVN: $!\n";
374                 }
375                 exit 1;
376         }
377         if (scalar(@conflicted) > 0)
378         {
379                 print STDERR "\e[0;33;1mERROR:\e[0m You have local modifications which conflicted with the updates from SVN\n";
380                 print STDERR "Configure is not able to complete the update. Please resolve these conflicts, then run ./configure -update\n";
381                 print "Conflicted files: " . join ", ", @conflicted . "\n";
382                 exit 1;
383         }
384         if ($configurechanged)
385         {
386                 system("perl configure -update");
387         }
388         else
389         {
390                 print "No need to update Makefiles.\n";
391         }
392         if (defined $opt_rebuild) {
393                 system("make install");
394         }
395         exit;
396 }
397
398 sub test_compile {
399         my $feature = shift;
400         my $fail = 0;
401         $fail ||= system "$config{CC} -o test_$feature make/check_$feature.cpp >/dev/null 2>&1";
402         $fail ||= system "./test_$feature";
403         unlink "test_$feature";
404         return !$fail;
405 }
406
407 print "Running non-interactive configure...\n" unless $interactive;
408 print "Checking for cache from previous configure... ";
409 print ((!getcache()) ? "not found\n" : "found\n");
410 $config{SYSTEM} = lc $^O;
411 print "Checking operating system version... $config{SYSTEM}\n";
412
413 printf "Checking if stdint.h exists... ";
414 $config{HAS_STDINT} = "true";
415 our $fail = 0;
416 open(STDINT, "</usr/include/stdint.h") or $config{HAS_STDINT} = "false";
417 if ($config{HAS_STDINT} eq "true") {
418         close(STDINT);
419 }
420 print "yes\n" if $config{HAS_STDINT} eq "true";
421 print "no\n" if $config{HAS_STDINT} eq "false";
422
423 printf "Checking if strlcpy exists... ";
424 # Perform the strlcpy() test..
425 $config{HAS_STRLCPY} = "false";
426 $fail = 0;
427 open(STRLCPY, "</usr/include/string.h") or $fail = 1;
428 if (!$fail) {
429         while (defined(my $line = <STRLCPY>)) {
430                 chomp($line);
431                 # try and find the delcaration of:
432                 # size_t strlcpy(...)
433                 if ($line =~ /size_t(\0x9|\s)+strlcpy/) {
434                         $config{HAS_STRLCPY} = "true";
435                 }
436         }
437         close(STRLCPY);
438 }
439 print "yes\n" if $config{HAS_STRLCPY} eq "true";
440 print "no\n" if $config{HAS_STRLCPY} eq "false";
441
442 printf "Checking if kqueue exists... ";
443 $has_kqueue = 0;
444 $fail = 0;
445 open(KQUEUE, "</usr/include/sys/event.h") or $fail = 1;
446 if (!$fail) {
447         while (defined(my $line = <KQUEUE>)) {
448                 chomp($line);
449                 # try and find the delcaration of:
450                 # int kqueue(void);
451                 if ($line =~ /int(\0x9|\s)+kqueue/) {
452                         $has_kqueue = 1;
453                 }
454         }
455         close(KQUEUE);
456 }
457 print "yes\n" if $has_kqueue == 1;
458 print "no\n" if $has_kqueue == 0;
459
460 printf "Checking for epoll support... ";
461 $has_epoll = test_compile('epoll');
462 print $has_epoll ? "yes\n" : "no\n";
463
464 printf "Checking for eventfd support... ";
465 $config{HAS_EVENTFD} = test_compile('eventfd') ? 'true' : 'false';
466 print $config{HAS_EVENTFD} eq 'true' ? "yes\n" : "no\n";
467
468 printf "Checking if Solaris I/O completion ports are available... ";
469 $has_ports = 0;
470 our $system = `uname -s`;
471 chomp ($system);
472 $has_ports = 1 if ($system eq "SunOS");
473
474 if ($has_ports) {
475         my $kernel = `uname -r`;
476         chomp($kernel);
477         if (($kernel !~ /^5\.1./)) {
478                 $has_ports = 0;
479         }
480 }
481 print "yes\n" if $has_ports == 1;
482 print "no\n" if $has_ports == 0;
483
484 $config{HAS_EPOLL} = $has_epoll;
485 $config{HAS_KQUEUE} = $has_kqueue;
486
487 printf "Checking for libgnutls... ";
488 if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTLS} eq "y"))) {
489         if (defined($gnutls_ver) && ($gnutls_ver ne "")) {
490                 print "yes\n";
491                 $config{HAS_GNUTLS} = "y";
492         } else {
493                 print "no\n";
494                 $config{HAS_GNUTLS} = "n";
495         }
496 } else {
497         print "no\n";
498         $config{HAS_GNUTLS} = "n";
499 }
500
501 printf "Checking for openssl... ";
502 if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPENSSL} eq "y"))) {
503         if (defined($openssl_ver) && ($openssl_ver ne "")) {
504                 print "yes\n";
505                 $config{HAS_OPENSSL} = "y";
506         } else {
507                 print "no\n";
508                 $config{HAS_OPENSSL} = "n";
509         }
510 } else {
511         print "no\n";
512         $config{HAS_OPENSSL} = "n";
513 }
514
515 printf "Checking if you are running an ancient, unsupported OS... ";
516 if ($config{OSNAME} =~ /FreeBSD/i)
517 {
518         my $version = `uname -r`;
519         if ($version =~ /^4\./)
520         {
521                 print "yes.\n";
522                 print "FreeBSD 4.x is no longer supported. By ANYONE.\n";
523                 print "To build, you will need to add the following to CXXFLAGS:\n";
524                 print "\t-L/usr/local/lib -lgnugetopt -DHAVE_DECL_GETOPT=1\n";
525         }
526         else
527         {
528                 print "no ($version)\n";
529         }
530 }
531 else
532 {
533         print "no ($config{OSNAME})\n";
534 }
535
536 print "Checking for upgrades to extra and third party modules... ";
537 system "./modulemanager upgrade";
538
539 ################################################################################
540 #                         BEGIN INTERACTIVE PART                              #
541 ################################################################################
542
543 # Clear the Screen..
544 if ($interactive)
545 {
546         print "\e[2J\e[0G\e[0d"; # J = Erase in Display, 2 = Entire Screen, (G, d) = Move cursor to (..,..)
547         my $wholeos = $^O;
548
549         my $rev = getrevision();
550         # Display Introduction Message..
551         print <<"STOP" ;
552 Welcome to the \e[1mInspIRCd\e[0m Configuration program! (\e[1minteractive mode\e[0m)
553 \e[1mPackage maintainers: Type ./configure --help for non-interactive help\e[0m
554
555 *** If you are unsure of any of these values, leave it blank for    ***
556 *** standard settings that will work, and your server will run      ***
557 *** using them. Please consult your IRC network admin if in doubt.  ***
558
559 Press \e[1m<RETURN>\e[0m to accept the default for any option, or enter
560 a new value. Please note: You will \e[1mHAVE\e[0m to read the docs
561 dir, otherwise you won't have a config file!
562
563 Your operating system is: \e[1;32m$config{OSNAME}\e[0m ($wholeos)
564 Your InspIRCd revision ID is \e[1;32mr$rev\e[0m
565 STOP
566         if ($rev eq "r0") {
567                 print " (Non-SVN build)";
568         }
569         print ".\n\n";
570
571         $config{CHANGE_COMPILER} = "n";
572         print "I have detected the following compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
573
574         while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) {
575                 print "\e[1;32mIMPORTANT!\e[0m A GCC 2.x compiler has been detected, and
576 should NOT be used. You should probably specify a newer compiler.\n\n";
577                 yesno('CHANGE_COMPILER',"Do you want to change the compiler?");
578                 if ($config{CHANGE_COMPILER} =~ /y/i) {
579                         print "What command do you want to use to invoke your compiler?\n";
580                         print "[\e[1;32m$config{CC}\e[0m] -> ";
581                         chomp($config{CC} = <STDIN>);
582                         if ($config{CC} eq "") {
583                                 $config{CC} = "g++";
584                         }
585                         chomp(my $foo = `$config{CC} -dumpversion | cut -c 1`);
586                         if ($foo ne "") {
587                                 chomp($config{GCCVER}       = `$config{CC} -dumpversion | cut -c 1`); # we must redo these if we change compilers
588                                 chomp($config{GCCMINOR}     = `$config{CC} -dumpversion | cut -c 3`);
589                                 print "Queried compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n";
590                                 if ($config{GCCVER} < 3) {
591                                         print "\e[1;32mGCC 2.x WILL NOT WORK!\e[0m. Let's try that again, shall we?\n";
592                                 }
593                         }
594                         else {
595                                 print "\e[1;32mWARNING!\e[0m Could not execute the compiler you specified. You may want to try again.\n";
596                         }
597                 }
598         }
599
600         print "\n";
601
602         # Directory Settings..
603         my $tmpbase = $config{BASE_DIR};
604         dir_check("do you wish to install the InspIRCd base", "BASE_DIR");
605         if ($tmpbase ne $config{BASE_DIR}) {
606                 $config{CONFIG_DIR}      = resolve_directory($config{BASE_DIR}."/conf");           # Configuration Dir
607                 $config{MODULE_DIR}      = resolve_directory($config{BASE_DIR}."/modules");     # Modules Directory
608                 $config{BINARY_DIR}      = resolve_directory($config{BASE_DIR}."/bin");     # Binary Directory
609         }
610
611         dir_check("are the configuration files", "CONFIG_DIR");
612         dir_check("are the modules to be compiled to", "MODULE_DIR");
613         dir_check("is the IRCd binary to be placed", "BINARY_DIR");
614         dir_check("do you want the build to take place", "BUILD_DIR");
615
616         my $chose_hiperf = 0;
617         if ($has_kqueue) {
618                 yesno('USE_KQUEUE',"You are running a BSD operating system, and kqueue\nwas detected. Would you like to enable kqueue support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable kqueue?");
619                 print "\n";
620                 if ($config{USE_KQUEUE} eq "y") {
621                         $chose_hiperf = 1;
622                 }
623         }
624         if ($has_epoll) {
625                 yesno('USE_EPOLL',"You are running a Linux 2.6+ operating system, and epoll\nwas detected. Would you like to enable epoll support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable epoll?");
626                 print "\n";
627                 if ($config{USE_EPOLL} eq "y") {
628                         $chose_hiperf = 1;
629                 }
630         }
631         if ($has_ports) {
632                 yesno('USE_PORTS',"You are running Solaris 10.\nWould you like to enable I/O completion ports support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable support for I/O completion ports?");
633                 print "\n";
634                 if ($config{USE_PORTS} eq "y") {
635                         $chose_hiperf = 1;
636                 }
637         }
638
639         if (!$chose_hiperf) {
640                 yesno('USE_POLL', "Would you like to use poll?\n This is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable poll?");
641                 if ($config{USE_POLL} ne "y")
642                 {
643                         print "No high-performance socket engines are available, or you chose\n";
644                         print "not to enable one. Defaulting to select() engine.\n\n";
645                 }
646         }
647
648         yesno('IPV6',"Would you like to build InspIRCd with IPv6 support?\nYou can still use IPv4 addresses in your port bindings.\n\nEnable IPv6?");
649         print "\n";
650
651         $config{USE_FREEBSD_BASE_SSL} = "n";
652         $config{USE_FREEBSD_PORTS_SSL} = "n";
653         if ($config{HAS_OPENSSL_PORT} ne "")
654         {
655                 $config{USE_FREEBSD_PORTS_SSL} = "y";
656                 print "I have detected the OpenSSL FreeBSD port installed on your system,\n";
657                 print "version \e[1;32m".$config{HAS_OPENSSL_PORT}."\e[0m. Your base system OpenSSL is version \e[1;32m".$openssl_ver."\e[0m.\n\n";
658                 yesno('USE_FREEBSD_PORTS_SSL', "Do you want to use the FreeBSD ports version?");
659                 print "\n";
660                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($config{USE_FREEBSD_PORTS_SSL} eq "n");
661
662                 if ($config{USE_FREEBSD_BASE_SSL} eq "n")
663                 {
664                         # update to port version
665                         $openssl_ver = $config{HAS_OPENSSL_PORT};
666                 }
667         }
668         else
669         {
670                 $config{USE_FREEBSD_BASE_SSL} = "y" if ($^O eq "freebsd");
671         }
672
673         $config{USE_SSL} = "n";
674
675         if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y")
676         {
677                 print "Detected GnuTLS version: \e[1;32m" . $gnutls_ver . "\e[0m\n";
678                 print "Detected OpenSSL version: \e[1;32m" . $openssl_ver . "\e[0m\n\n";
679
680                 yesno('USE_SSL', "One or more SSL libraries detected. Would you like to enable SSL support?");
681                 if ($config{USE_SSL} eq "y")
682                 {
683                         if ($config{HAS_GNUTLS} eq "y")
684                         {
685                                 yesno('USE_GNUTLS',"Would you like to enable SSL with m_ssl_gnutls? (recommended)");
686                                 if ($config{USE_GNUTLS} eq "y")
687                                 {
688                                         print "\nUsing GnuTLS SSL module.\n";
689                                 }
690                         }
691
692                         if ($config{HAS_OPENSSL} eq "y")
693                         {
694                                 yesno('USE_OPENSSL', "Would you like to enable SSL with m_ssl_openssl?");
695                                 if ($config{USE_OPENSSL} eq "y")
696                                 {
697                                         print "\nUsing OpenSSL SSL module.\nYou will get better performance if you move to GnuTLS in the future.\n";
698                                 }
699                         }
700                 }
701         }
702         else
703         {
704                 print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed if\n";
705                 print "you intend to use OpenSSL, or that GnuTLS is in your path if you intend\nto use GnuTLS.\n\n";
706         }
707 }
708
709 dumphash();
710
711 if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y"))
712 {
713         print "Sorry, but i couldn't detect gnutls. Make sure gnutls-config is in your path.\n";
714         exit(0);
715 }
716 if (($config{USE_OPENSSL} eq "y") && ($config{HAS_OPENSSL} ne "y"))
717 {
718         print "Sorry, but i couldn't detect openssl. Make sure openssl is in your path.\n";
719         exit(0);
720 }
721 our $failed = 0;
722
723 $config{CERTGEN} ||= 'y';
724 yesno('CERTGEN',"Would you like generate SSL certificates now?") if ($interactive && ($config{USE_GNUTLS} eq "y" || $config{USE_OPENSSL} eq "y"));
725
726 if ($config{USE_GNUTLS} eq "y") {
727         unless (-r "src/modules/m_ssl_gnutls.cpp") {
728                 print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n";
729                 symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!";
730         }
731         if ($interactive && $config{CERTGEN} eq 'y')
732         {
733                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
734                         print "SSL Certificates Not found, Generating.. \n\n
735 *************************************************************
736 * Generating the Private Key may take some time, go grab a  *
737 * Coffee. Even better, to generate some more entropy if it  *
738 * is taking a while, open another console and type du / a   *
739 * few times and get that HD going :) Then answer the        *
740 * Questions which follow. If you are unsure, just hit enter *
741 *************************************************************\n\n";
742                         $failed = make_gnutls_cert();
743                         if ($failed) {
744                                 print "\n\e[1;32mCertificate generation failed!\e[0m\n\n";
745                         } else {
746                                 print "\nCertificate generation complete, copying to config directory... ";
747                                 File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
748                                 File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
749                                 print "Done.\n\n";
750                         }
751                 }
752                 else {
753                         print "SSL Certificates found, skipping.\n\n";
754                 }
755         }
756         else
757         {
758                 print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
759         }
760 }
761
762 if ($config{USE_OPENSSL} eq "y") {
763         unless (-r "src/modules/m_ssl_openssl.cpp") {
764                 print "Symlinking src/modules/m_ssl_openssl.cpp from extra/\n";
765                 symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!";
766         }
767         $failed = 0;
768         if ($interactive && $config{CERTGEN} eq 'y')
769         {
770                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") {
771                         print "SSL Certificates Not found, Generating.. \n\n
772 *************************************************************
773 * Generating the certificates may take some time, go grab a *
774 * coffee, or something.                                     *
775 *************************************************************\n\n";
776                         make_openssl_cert();
777                         print "\nCertificate generation complete, copying to config directory... ";
778                         File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
779                         File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
780                         File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n";
781                         print "Done.\n\n";
782                 } else {
783                         print "SSL Certificates found, skipping.\n\n"
784                 }
785         }
786         else
787         {
788                 print "Skipping SSL certificate generation\nin non-interactive mode.\n\n";
789         }
790 }
791 if (($config{USE_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) {
792         print "Skipping SSL Certificate generation, SSL support is not available.\n\n";
793 }
794
795 depcheck();
796 writefiles(1);
797 makecache();
798
799 print "\n\n";
800 print "To build your server with these settings, please run '\e[1;32mmake\e[0m' now.\n";
801 if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) {
802         print "Please note: for \e[1;32mSSL support\e[0m you will need to load required\n";
803         print "modules in your config. This configure script has added those modules to the\n";
804         print "build process. For more info please refer to:\n";
805         print "\e[1;32mhttp://wiki.inspircd.org/Installation_From_Tarball\e[0m\n";
806 }
807 print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n\n";
808 if (($config{OSNAME} eq "OpenBSD") && ($config{CC} ne "eg++")) {
809         print "\e[1;32mWARNING!\e[0m You are running OpenBSD but you are using the base gcc package\nrather than eg++. This compile will most likely fail, but i'm letting you\ngo ahead with it anyway, just in case i'm wrong :-)\n";
810 }
811
812 if ($config{GCCVER} < "3") {
813         print <<FOO2;
814 \e[1;32mWARNING!\e[0m You are attempting to compile InspIRCd on GCC 2.x!
815 GCC 2.x series compilers only had partial (read as broken) C++ support, and
816 your compile will most likely fail horribly! If you have any problems, do NOT
817 report them to the bugtracker or forums without first upgrading your compiler
818 to a newer 3.x or 4.x (or whatever is available currently) version.
819 FOO2
820 }
821
822 ################################################################################
823 #                             HELPER FUNCTIONS                          #
824 ################################################################################
825 sub getcache {
826         # Retrieves the .config.cache file, and loads values into the main config hash.
827         open(CACHE, ".config.cache") or return 0;
828         while (<CACHE>) {
829                 chomp;
830                 # Ignore Blank lines, and comments..
831                 next if /^\s*$/;
832                 next if /^\s*#/;
833                 my ($key, $value) = split("=", $_, 2);
834                 $value =~ /^\"(.*)\"$/;
835                 # Do something with data here!
836                 $config{$key} = $1;
837         }
838         close(CACHE);
839         return 1;
840 }
841
842 sub makecache {
843         # Dump the contents of %config
844         print "Writing \e[1;32mcache file\e[0m for future ./configures ...\n";
845         open(FILEHANDLE, ">.config.cache");
846         foreach my $key (keys %config) {
847                 print FILEHANDLE "$key=\"$config{$key}\"\n";
848         }
849         close(FILEHANDLE);
850 }
851
852 sub dir_check {
853         my ($desc, $hash_key) = @_;
854         my $complete = 0;
855         while (!$complete) {
856                 print "In what directory $desc?\n";
857                 print "[\e[1;32m$config{$hash_key}\e[0m] -> ";
858                 chomp(my $var = <STDIN>);
859                 if ($var eq "") {
860                         $var = $config{$hash_key};
861                 }
862                 if ($var =~ /^\~\/(.+)$/) {
863                         # Convert it to a full path..
864                         $var = resolve_directory($ENV{HOME} . "/" . $1);
865                 }
866                 elsif ((($config{OSNAME} =~ /MINGW32/i) and ($var !~ /^[A-Z]{1}:\\.*/)) and (substr($var,0,1) ne "/"))
867                 {
868                         # Assume relative Path was given.. fill in the rest.
869                         $var = $this . "/$var";
870                 }
871
872                 $var = resolve_directory($var);
873                 if (! -e $var) {
874                         print "$var does not exist. Create it?\n[\e[1;32my\e[0m] ";
875                         chomp(my $tmp = <STDIN>);
876                         if (($tmp eq "") || ($tmp =~ /^y/i)) {
877                                 # Attempt to Create the Dir..
878                                 my $chk = eval {
879                                         use File::Path ();
880                                         File::Path::mkpath($var, 0, 0777);
881                                         1;
882                                 };
883                                 unless (defined($chk) && -d $var) {
884                                         print "Unable to create directory. ($var)\n\n";
885                                         # Restart Loop..
886                                         next;
887                                 }
888                         } else {
889                                 # They said they don't want to create, and we can't install there.
890                                 print "\n\n";
891                                 next;
892                         }
893                 } else {
894                         if (!is_dir($var)) {
895                                 # Target exists, but is not a directory.
896                                 print "File $var exists, but is not a directory.\n\n";
897                                 next;
898                         }
899                 }
900                 # Either Dir Exists, or was created fine.
901                 $config{$hash_key} = $var;
902                 $complete = 1;
903                 print "\n";
904         }
905 }
906
907 our $SHARED = "";
908
909 my ($mliflags, $mfrules, $mobjs, $mfcount) = ("", "", "", 0);
910
911 sub writefiles {
912         my($writeheader) = @_;
913         # First File.. inspircd_config.h
914         chomp(my $incos = `uname -n -s -r`);
915         chomp(my $version = `sh src/version.sh`);
916         chomp(my $revision2 = getrevision());
917         if ($writeheader == 1)
918         {
919                 print "Writing \e[1;32minspircd_config.h\e[0m\n";
920                 open(FILEHANDLE, ">include/inspircd_config.h.tmp");
921                 print FILEHANDLE <<EOF;
922 /* Auto generated by configure, do not modify! */
923 #ifndef __CONFIGURATION_AUTO__
924 #define __CONFIGURATION_AUTO__
925
926 /* this is for windows support. */
927 #define CoreExport /**/
928 #define DllExport /**/
929
930 #define CONFIG_FILE "$config{CONFIG_DIR}/inspircd.conf"
931 #define MOD_PATH "$config{MODULE_DIR}"
932 #define SOMAXCONN_S "$config{_SOMAXCONN}"
933 #define ENTRYPOINT int main(int argc, char** argv)
934
935 EOF
936 print FILEHANDLE "#define MAXBUF " . ($config{MAXBUF}+2) . "\n";
937
938                 if ($config{OSNAME} =~ /SunOS/i) {
939                         print FILEHANDLE "#define IS_SOLARIS\n";
940                 }
941                 if ($config{OSNAME} =~ /MINGW32/i) {
942                         print FILEHANDLE "#define IS_MINGW\n";
943                 }
944                 if ($config{GCCVER} >= 3) {
945                         print FILEHANDLE "#define GCC3\n";
946                 }
947                 if (
948                         (($config{GCCVER} == 4) && ($config{GCCMINOR} >= 3))
949                                 ||
950                         ($config{GCCVER} > 4)
951                 ) {
952                         print FILEHANDLE "#define HASHMAP_DEPRECATED\n";
953                 }
954                 if ($config{HAS_STRLCPY} eq "true") {
955                         print FILEHANDLE "#define HAS_STRLCPY\n";
956                 }
957                 if ($config{HAS_STDINT} eq "true") {
958                         print FILEHANDLE "#define HAS_STDINT\n";
959                 }
960                 if ($config{IPV6} =~ /y/i) {
961                         print FILEHANDLE "#define IPV6\n";
962                 }
963                 if ($config{HAS_EVENTFD} eq 'true') {
964                         print FILEHANDLE "#define HAS_EVENTFD\n";
965                 }
966                 my $use_hiperf = 0;
967                 if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) {
968                         print FILEHANDLE "#define USE_KQUEUE\n";
969                         $config{SOCKETENGINE} = "socketengine_kqueue";
970                         $use_hiperf = 1;
971                 }
972                 if (($has_epoll) && ($config{USE_EPOLL} eq "y")) {
973                         print FILEHANDLE "#define USE_EPOLL\n";
974                         $config{SOCKETENGINE} = "socketengine_epoll";
975                         $use_hiperf = 1;
976                 }
977                 if (($has_ports) && ($config{USE_PORTS} eq "y")) {
978                         print FILEHANDLE "#define USE_PORTS\n";
979                         $config{SOCKETENGINE} = "socketengine_ports";
980                         $use_hiperf = 1;
981                 }
982                 # user didn't choose either epoll or select for their OS.
983                 # default them to USE_SELECT (ewwy puke puke)
984                 if (!$use_hiperf) {
985                         print "no hi-perf, " . $config{USE_POLL};
986                         if ($config{USE_POLL} eq "y")
987                         {
988                                 print FILEHANDLE "#define USE_POLL\n";
989                                 $config{SOCKETENGINE} = "socketengine_poll";
990                         }
991                         else
992                         {
993                                 print FILEHANDLE "#define USE_SELECT\n";
994                                 $config{SOCKETENGINE} = "socketengine_select";
995                         }
996                 }
997                 print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n\n#endif\n";
998                 close(FILEHANDLE);
999
1000                 open(FILEHANDLE, ">include/inspircd_se_config.h.tmp");
1001                 print FILEHANDLE <<EOF;
1002 /* Auto generated by configure, do not modify or commit to svn! */
1003 #ifndef __CONFIGURATION_SOCKETENGINE__
1004 #define __CONFIGURATION_SOCKETENGINE__
1005
1006 #include "socketengines/$config{SOCKETENGINE}.h"
1007
1008 #endif
1009 EOF
1010                 close(FILEHANDLE);
1011
1012                 open(FILEHANDLE, ">include/inspircd_version.h.tmp");
1013                 print FILEHANDLE <<EOF;
1014 #define VERSION "$version"
1015 #define REVISION "$revision2"
1016 #define SYSTEM "$incos"
1017 EOF
1018                 close FILEHANDLE;
1019
1020                 for my $file (qw(include/inspircd_config.h include/inspircd_se_config.h include/inspircd_version.h)) {
1021                         my $diff = 0;
1022                         open my $fh1, $file or $diff = 1;
1023                         open my $fh2, $file.'.tmp' or die "Can't read $file.tmp that we just wrote: $!";
1024                         while (!$diff) {
1025                                 my $line1 = <$fh1>;
1026                                 my $line2 = <$fh2>;
1027                                 if (defined($line1) != defined($line2)) {
1028                                         $diff = 1;
1029                                 } elsif (!defined $line1) {
1030                                         last;
1031                                 } else {
1032                                         $diff = ($line1 ne $line2);
1033                                 }
1034                         }
1035                         if ($diff) {
1036                                 unlink $file;
1037                                 rename "$file.tmp", $file;
1038                         } else {
1039                                 unlink "$file.tmp";
1040                         }
1041                 }
1042         }
1043
1044         # Write all .in files.
1045         my $tmp = "";
1046         my $file = "";
1047         my $exe = "inspircd";
1048
1049         # Do this once here, and cache it in the .*.inc files,
1050         # rather than attempting to read src/version.sh from
1051         # compiled code -- we might not have the source to hand.
1052         # Fix for bug#177 by Brain.
1053
1054         chomp($version = `sh ./src/version.sh`);
1055         chomp(my $revision = getrevision());
1056         $version = "$version(r$revision)";
1057
1058         # We can actually parse any file starting with . and ending with .inc,
1059         # but right now we only parse .inspircd.inc to form './inspircd'
1060         prepare_dynamic_makefile();
1061
1062     print "Writing \e[1;32mMakefiles\e[0m\n";
1063
1064         opendir(DIRHANDLE, $this);
1065
1066         foreach my $name (sort readdir(DIRHANDLE)) {
1067                 if ($name =~ /^\.(.+)\.inc$/) {
1068                         $file = $1;
1069
1070                         # Bug #353, omit this on non-darwin
1071                         next if (($config{OSNAME} !~ /darwin/) && ($file eq "org.inspircd.plist"));
1072
1073                         # All .name.inc files need parsing!
1074                         open(FILEHANDLE, ".$file.inc") or die ("Can't open .$file.inc");
1075                         $_ = join '', <FILEHANDLE>;
1076                         close(FILEHANDLE);
1077
1078                         print "Writing \e[1;32m$file\e[0m ...\n";
1079
1080                         $config{BUILD_DIR} ||= 'build';
1081
1082                         for my $var (qw(
1083                                 CC SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR
1084                                 STARTSCRIPT DESTINATION EXTRA_DIR SOCKETENGINE
1085                         )) {
1086                                 s/\@$var\@/$config{$var}/g;
1087                         }
1088                         s/\@EXECUTABLE\@/$exe/ if defined $exe;
1089                         s/\@VERSION\@/$version/ if defined $version;
1090
1091                         if ($file eq 'Makefile') {
1092                                 my $mk_tmp = $_;
1093                                 s/\@IFDEF (\S+)/ifdef $1/g;
1094                                 s/\@IFNDEF (\S+)/ifndef $1/g;
1095                                 s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
1096                                 s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
1097                                 s/\@ELSE/else/g;
1098                                 s/\@ENDIF/endif/g;
1099                                 s/ *\@BSD_ONLY .*\n//g;
1100                                 s/\@GNU_ONLY //g;
1101                                 s/\@DO_EXPORT (.*)/export $1/g;
1102                                 open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
1103                                 print MKF $_;
1104                                 close MKF;
1105                                 $_ = $mk_tmp;
1106                                 s/\@IFDEF (\S+)/.if defined($1)/g;
1107                                 s/\@IFNDEF (\S+)/.if !defined($1)/g;
1108                                 s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g;
1109                                 s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g;
1110                                 s/\@ELSE/.else/g;
1111                                 s/\@ENDIF/.endif/g;
1112                                 s/\@BSD_ONLY //g;
1113                                 s/ *\@GNU_ONLY .*\n//g;
1114                                 $mk_tmp = $_;
1115                                 $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg;
1116                                 open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!";
1117                                 print MKF $mk_tmp;
1118                                 close MKF;
1119                         } else {
1120                                 open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n");
1121                                 print FILEHANDLE $_;
1122                                 close(FILEHANDLE);
1123                         }
1124                 }
1125         }
1126         closedir(DIRHANDLE);
1127
1128         # Make inspircd executable!
1129         chmod 0744, 'inspircd';
1130 }
1131
1132 sub depcheck
1133 {
1134         getmodules();
1135         for my $mod (@modlist) {
1136                 getcompilerflags("src/modules/m_$mod.cpp");
1137                 getlinkerflags("src/modules/m_$mod.cpp");
1138         }
1139 }
1140
1141 sub prepare_dynamic_makefile
1142 {
1143         my $i = 0;
1144
1145         if (!$has_epoll)
1146         {
1147                 $config{USE_EPOLL} = 0;
1148         }
1149         if (!$has_kqueue)
1150         {
1151                 $config{USE_KQUEUE} = 0;
1152         }
1153         if (!$has_ports)
1154         {
1155                 $config{USE_PORTS} = 0;
1156         }
1157 }
1158
1159 # Routine to list out the extra/ modules that have been enabled.
1160 # Note: when getting any filenames out and comparing, it's important to lc it if the
1161 # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
1162 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
1163 # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
1164 sub list_extras () {
1165         use File::Spec;
1166         # @_ not used
1167         my $srcdir = File::Spec->catdir("src", "modules");
1168         my $abs_srcdir = File::Spec->rel2abs($srcdir);
1169         local $_;
1170         my $dd;
1171         opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
1172         my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1173         closedir $dd;
1174         undef $dd;
1175         opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
1176         my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
1177         closedir $dd;
1178         undef $dd;
1179         my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
1180         my %extras = ();
1181 EXTRA:  for my $extra (@extras) {
1182                 next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
1183                 next if ($extra eq '.svn');
1184                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1185                 my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
1186                 next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
1187                 if (-l $abs_source) {
1188                         # Symlink, is it in the right place?
1189                         my $targ = readlink($abs_source);
1190                         my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
1191                         if ($abs_targ eq $abs_extra) {
1192                                 $extras{$extra} = "\e[32;1menabled\e[0m";
1193                         } else {
1194                                 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
1195                         }
1196                 } elsif (-e $abs_source) {
1197                         my ($devext, $inoext) = stat($abs_extra);
1198                         my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
1199                         if ($lnksrc > 1) {
1200                                 if ($devsrc == $devext && $inosrc == $inoext) {
1201                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1202                                 } else {
1203                                         $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
1204                                 }
1205                         } else {
1206                                 open my $extfd, "<", $abs_extra;
1207                                 open my $srcfd, "<", $abs_source;
1208                                 local $/ = undef;
1209                                 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
1210                                         $extras{$extra} = "\e[32;1menabled\e[0m";
1211                                 } else {
1212                                         $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
1213                                 }
1214                         }
1215                 } else {
1216                         $extras{$extra} = "\e[33;1mdisabled\e[0m";
1217                 }
1218         }
1219         # Now let's add dependency info
1220         for my $extra (keys(%extras)) {
1221                 next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
1222                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
1223                 my @deps = split / +/, getdependencies($abs_extra);
1224                 for my $dep (@deps) {
1225                         if (exists($extras{$dep})) {
1226                                 my $ref = \$extras{$dep}; # Take reference.
1227                                 if ($$ref !~ m/needed by/) {
1228                                         # First dependency found.
1229                                         if ($$ref =~ m/enabled/) {
1230                                                 $$ref .= " (needed by \e[32;1m$extra\e[0m";
1231                                         } else {
1232                                                 $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later.
1233                                                 $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m";
1234                                         }
1235                                 } else {
1236                                         if ($$ref =~ m/enabled/) {
1237                                                 $$ref .= ", \e[32;1m$extra\e[0m";
1238                                         } else {
1239                                                 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
1240                                         }
1241                                 }
1242                         }
1243                 }
1244         }
1245         for my $extra (sort {$a cmp $b} keys(%extras)) {
1246                 my $text = $extras{$extra};
1247                 if ($text =~ m/needed by/ && $text !~ m/enabled/) {
1248                         printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
1249                 } else {
1250                         printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
1251                 }
1252         }
1253         return keys(%extras) if wantarray; # Can be used by manage_extras.
1254 }
1255
1256 sub enable_extras (@) {
1257         my (@extras) = @_;
1258         for my $extra (@extras) {
1259                 my $extrapath = "src/modules/extra/$extra";
1260                 if (!-e $extrapath) {
1261                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
1262                         next;
1263                 }
1264                 my $source = "src/modules/$extra";
1265                 if (-e $source) {
1266                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
1267                         next;
1268                 }
1269                 # Get dependencies, and add them to be processed.
1270                 my @deps = split / +/, getdependencies($extrapath);
1271                 for my $dep (@deps) {
1272                         next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
1273                         if (!-e "src/modules/$dep") {
1274                                 if (-e "src/modules/extra/$dep") {
1275                                         print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
1276                                         push @extras, $dep;
1277                                 } else {
1278                                         print STDERR "\e[33;1mWARNING:\e[0m module \e[32;1m$extra\e[0m might be missing dependency \e[32;1m$dep\e[0m - YOU are responsible for satisfying it!\n";
1279                                 }
1280                         }
1281                 }
1282                 print "Enabling $extra ... \n";
1283                 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
1284         }
1285 }
1286
1287 sub disable_extras (@)
1288 {
1289         opendir my $dd, "src/modules/extra/";
1290         my @files = readdir($dd);
1291         closedir $dd;
1292         my (@extras) = @_;
1293 EXTRA:  for my $extra (@extras) {
1294                 my $extrapath = "src/modules/extra/$extra";
1295                 my $source = "src/modules/$extra";
1296                 if (!-e $extrapath) {
1297                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
1298                         next;
1299                 }
1300                 if ((! -l $source) || readlink($source) ne "extra/$extra") {
1301                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : Source is not a link or doesn't refer to the right file. Remove manually if this is in error.\n";
1302                         next;
1303                 }
1304                 # Check if anything needs this.
1305                 for my $file (@files) {
1306                         my @deps = split / +/, getdependencies("src/modules/extra/$file");
1307                         # File depends on this extra...
1308                         if (scalar(grep { $_ eq $extra } @deps) > 0) {
1309                                 # And is both enabled and not about to be disabled.
1310                                 if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
1311                                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
1312                                         next EXTRA;
1313                                 }
1314                         }
1315                 }
1316                 # Now remove.
1317                 print "Disabling $extra ... \n";
1318                 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";
1319         }
1320 }