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