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