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