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