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