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