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