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