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