]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - configure
2f92d9f168a57df7f1f1a68da833f2e09bc26f58
[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 File::Spec::Functions qw(rel2abs);
38 use Cwd;
39 use Getopt::Long;
40
41 use make::configure;
42 use make::utilities;
43
44 our ($opt_use_gnutls, $opt_use_openssl, $opt_nointeractive, $opt_socketengine,
45      $opt_system, $opt_uid, $opt_base_dir, $opt_config_dir, $opt_module_dir, $opt_binary_dir,
46      $opt_data_dir, $opt_log_dir);
47
48 sub list_extras ();
49
50 sub enable_extras (@);
51
52 sub disable_extras (@);
53
54 my @opt_enableextras;
55 my @opt_disableextras;
56
57 GetOptions (
58         'enable-gnutls' => \$opt_use_gnutls,
59         'system' => \$opt_system,
60         'uid=s' => \$opt_uid,
61         'enable-openssl' => \$opt_use_openssl,
62         'disable-interactive' => \$opt_nointeractive,
63         'socketengine=s' => \$opt_socketengine,
64         'prefix=s' => \$opt_base_dir,
65         'config-dir=s' => \$opt_config_dir,
66         'module-dir=s' => \$opt_module_dir,
67         'binary-dir=s' => \$opt_binary_dir,
68         'data-dir=s' => \$opt_data_dir,
69         'log-dir=s' => \$opt_log_dir,
70         'help'  => \&cmd_help,
71         'update' => \&cmd_update,
72         'clean' => \&cmd_clean,
73         'list-extras' => sub { list_extras; exit 0; }, # This, --enable-extras, and --disable-extras are for non-interactive managing.
74         'enable-extras=s@' => \@opt_enableextras, # ^
75         'disable-extras=s@' => \@opt_disableextras, # ^
76 );
77
78 if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
79         @opt_enableextras = split /,/, join(',', @opt_enableextras);
80         @opt_disableextras = split /,/, join(',', @opt_disableextras);
81         enable_extras(@opt_enableextras);
82         disable_extras(@opt_disableextras);
83         list_extras;
84         print "Remember: YOU are responsible for making sure any libraries needed have been installed!\n";
85         exit 0;
86 }
87
88 our $interactive = !(
89         (defined $opt_base_dir) ||
90         (defined $opt_config_dir) ||
91         (defined $opt_module_dir) ||
92         (defined $opt_base_dir) ||
93         (defined $opt_binary_dir) ||
94         (defined $opt_data_dir) ||
95         (defined $opt_log_dir) ||
96         (defined $opt_nointeractive) ||
97         (defined $opt_socketengine) ||
98         (defined $opt_use_openssl) ||
99         (defined $opt_system) ||
100         (defined $opt_uid) ||
101         (defined $opt_use_gnutls)
102 );
103
104 our $topdir = getcwd();
105 our %config = read_configure_cache();
106
107 print "Checking for cache from previous configure... ";
108 print %config ? "found\n" : "not found\n";
109
110 $config{BASE_DIR} = $topdir."/run";
111
112 if (defined $opt_base_dir) {
113         $config{BASE_DIR} = $opt_base_dir;
114 } elsif (defined $opt_system) {
115         $config{BASE_DIR} = '/var/lib/inspircd';
116 }
117
118 if (defined $opt_system) {
119         $config{UID} = defined $opt_uid ? $opt_uid : 'ircd';
120         $config{CONFIG_DIR} = '/etc/inspircd';
121         $config{MODULE_DIR} = '/usr/lib/inspircd';
122         $config{BINARY_DIR} = '/usr/sbin/';
123         $config{BUILD_DIR} = $topdir."/build";
124         $config{DATA_DIR} = '/var/inspircd';
125         $config{LOG_DIR} = '/var/log/inspircd';
126 } else {
127         $config{UID} = defined $opt_uid ? $opt_uid : $<;
128         $config{CONFIG_DIR} = rel2abs($config{BASE_DIR}."/conf");
129         $config{MODULE_DIR} = rel2abs($config{BASE_DIR}."/modules");
130         $config{BINARY_DIR} = rel2abs($config{BASE_DIR}."/bin");
131         $config{BUILD_DIR} = rel2abs($topdir."/build");
132         $config{DATA_DIR} = rel2abs($config{BASE_DIR}."/data");
133         $config{LOG_DIR} = rel2abs($config{BASE_DIR}."/logs");
134 }
135
136 if (defined $opt_config_dir) {
137         $config{CONFIG_DIR} = $opt_config_dir;
138 }
139 if (defined $opt_module_dir) {
140         $config{MODULE_DIR} = $opt_module_dir;
141 }
142 if (defined $opt_binary_dir) {
143         $config{BINARY_DIR} = $opt_binary_dir;
144 }
145 if (defined $opt_data_dir) {
146         $config{DATA_DIR} = $opt_data_dir;
147 }
148 if (defined $opt_log_dir) {
149         $config{LOG_DIR} = $opt_log_dir;
150 }
151 chomp($config{HAS_GNUTLS}   = `pkg-config --modversion gnutls 2>/dev/null`);
152 chomp($config{HAS_OPENSSL}  = `pkg-config --modversion openssl 2>/dev/null`);
153
154 chomp(our $gnutls_ver = $config{HAS_GNUTLS});
155 chomp(our $openssl_ver = $config{HAS_OPENSSL});
156 $config{USE_GNUTLS}         = 0;
157 if (defined $opt_use_gnutls)
158 {
159         $config{USE_GNUTLS} = "y";                                      # Use gnutls.
160 }
161 $config{USE_OPENSSL}    = 0;                                            # Use openssl.
162 if (defined $opt_use_openssl)
163 {
164         $config{USE_OPENSSL} = "y";
165 }
166
167 $config{STARTSCRIPT} = $^O eq 'darwin' ? 'org.inspircd.plist' : 'inspircd';
168
169 $config{CXX} = defined $ENV{CXX} && !system("$ENV{CXX} -v > /dev/null 2>&1") ? $ENV{CXX} : find_compiler();
170 if ($config{CXX} eq "") {
171         print "A C++ compiler could not be detected on your system!\n";
172         print "Set the CXX environment variable to the full path if this is incorrect.\n";
173         exit 1; 
174 }
175
176 our %cxx = get_compiler_info($config{CXX});
177 if ($cxx{UNSUPPORTED}) {
178         print "Your C++ compiler is too old to build InspIRCd!\n";
179         print "Reason: $cxx{REASON}\n";
180         exit 1;
181 }
182
183 if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)(?:[a-z])?(?:\-[a-z][0-9])?/) {
184         $config{HAS_OPENSSL} = $1;
185 } else {
186         $config{HAS_OPENSSL} = "";
187 }
188
189 $config{HAS_CLOCK_GETTIME} = run_test 'clock_gettime()', test_file($config{CXX}, 'clock_gettime.cpp', '-lrt');
190 $config{HAS_EVENTFD} = run_test 'eventfd()', test_file($config{CXX}, 'eventfd.cpp');
191
192 if ($config{HAS_EPOLL} = run_test 'epoll', test_header($config{CXX}, 'sys/epoll.h')) {
193         $config{SOCKETENGINE} ||= 'epoll';
194 }
195
196 if ($config{HAS_KQUEUE} = run_test 'kqueue', test_file($config{CXX}, 'kqueue.cpp')) {
197         $config{SOCKETENGINE} ||= 'kqueue';
198 }
199
200 if ($config{HAS_PORTS} = run_test 'Solaris IOCP', test_header($config{CXX}, 'port.h')) {
201         $config{SOCKETENGINE} ||= 'ports';
202 }
203
204 if ($config{HAS_POLL} = run_test 'poll', test_header($config{CXX}, 'poll.h')) {
205         $config{SOCKETENGINE} ||= 'poll';
206 }
207
208 # Select is available on all platforms
209 $config{HAS_SELECT} = 1;
210 $config{SOCKETENGINE} ||= "select";
211
212 if (defined $opt_socketengine) {
213         my $cfgkey = "HAS_" . uc $opt_socketengine;
214         if ($config{$cfgkey} && -f "src/socketengines/socketengine_$opt_socketengine.cpp") {
215                 $config{SOCKETENGINE} = $opt_socketengine;
216         } else {
217                 print "Unable to use a socket engine which is not supported on this platform ($opt_socketengine)!\n";
218                 print "Available socket engines are:";
219                 foreach (<src/socketengines/socketengine_*.cpp>) {
220                         s/src\/socketengines\/socketengine_(\w+)\.cpp/$1/;
221                         print " $1" if $config{"HAS_" . uc $1};
222                 }
223                 print "\n";     
224                 exit 1;
225         }
226 }
227
228 print "Checking for libgnutls... ";
229 if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTLS} eq "y"))) {
230         if (defined($gnutls_ver) && ($gnutls_ver ne "")) {
231                 print "yes\n";
232                 $config{HAS_GNUTLS} = "y";
233         } else {
234                 print "no\n";
235                 $config{HAS_GNUTLS} = "n";
236         }
237 } else {
238         print "no\n";
239         $config{HAS_GNUTLS} = "n";
240 }
241
242 print "Checking for openssl... ";
243 if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPENSSL} eq "y"))) {
244         if (defined($openssl_ver) && ($openssl_ver ne "")) {
245                 print "yes\n";
246                 $config{HAS_OPENSSL} = "y";
247         } else {
248                 print "no\n";
249                 $config{HAS_OPENSSL} = "n";
250         }
251 } else {
252         print "no\n";
253         $config{HAS_OPENSSL} = "n";
254 }
255
256 if ($interactive)
257 {
258         # Clear the screen.
259         system 'tput', 'clear';
260
261         my $revision = get_revision();
262         chomp(my $version = `sh src/version.sh`);
263
264         # Display Introduction Message..
265         print <<"STOP" ;
266 Welcome to the \e[1mInspIRCd\e[0m Configuration program! (\e[1minteractive mode\e[0m)
267 \e[1mPackage maintainers: Type ./configure --help for non-interactive help\e[0m
268
269 *** If you are unsure of any of these values, leave it blank for    ***
270 *** standard settings that will work, and your server will run      ***
271 *** using them. Please consult your IRC network admin if in doubt.  ***
272
273 Press \e[1m<RETURN>\e[0m to accept the default for any option, or enter
274 a new value. Please note: You will \e[1mHAVE\e[0m to read the docs
275 dir, otherwise you won't have a config file!
276
277 Your operating system is: \e[1;32m$^O\e[0m 
278 STOP
279         print "Your InspIRCd version is: \e[1;32m";
280         print $revision eq 'release' ? substr($version, 9) : substr($revision, 1);
281         print "\e[0m\n\n";
282         print "The following compiler has been detected: \e[1;32m$cxx{NAME} $cxx{VERSION}\e[0m ($config{CXX})\n\n";
283
284         # Check that the user actually wants this version.
285         if (index($version, '+') != -1) {
286                 print <<"EOW" ;
287 \e[1;31mWARNING!\e[0m You are building a development version. This contains code which has
288 not been tested as heavily and may contain various faults which could seriously
289 affect the running of your server. It is recommended that you use a stable
290 version instead.
291
292 You can obtain the latest stable version from https://github.com/inspircd/inspircd/releases
293 or by running `git checkout insp20` if you are installing from Git.
294
295 EOW
296         exit 1 unless prompt_bool(1, 'I understand this warning and want to continue anyway.', 0);
297         }
298
299         # Directory Settings..
300         my $tmpbase = $config{BASE_DIR};
301         $config{BASE_DIR} = prompt_dir(1, 'What directory do you wish to install the InspIRCd base?', $config{BASE_DIR});
302         if ($tmpbase ne $config{BASE_DIR}) {
303                 $config{CONFIG_DIR} = rel2abs($config{BASE_DIR}."/conf");
304                 $config{MODULE_DIR} = rel2abs($config{BASE_DIR}."/modules");
305                 $config{DATA_DIR} = rel2abs($config{BASE_DIR}."/data");
306                 $config{LOG_DIR} = rel2abs($config{BASE_DIR}."/logs");
307                 $config{BINARY_DIR} = rel2abs($config{BASE_DIR}."/bin");
308         }
309
310         $config{BINARY_DIR} = prompt_dir(1, 'In what directory should the InspIRCd binary be placed?', $config{BINARY_DIR});
311         $config{CONFIG_DIR} = prompt_dir(1, 'In what directory are the configuration files to be stored?', $config{CONFIG_DIR});
312         $config{DATA_DIR} = prompt_dir(1, 'In what directory are variable data files to be stored?', $config{DATA_DIR});
313         $config{LOG_DIR} = prompt_dir(1, 'In what directory are log files to be stored?', $config{LOG_DIR});
314         $config{MODULE_DIR} = prompt_dir(1, 'In what directory are the modules to be placed?', $config{MODULE_DIR});
315         $config{BUILD_DIR} = prompt_dir(1, 'In what directory do you want the build to take place?', $config{BUILD_DIR});
316
317         my $chose_hiperf = 0;
318         if ($config{HAS_KQUEUE}) {
319                 $config{USE_KQUEUE} = prompt_bool(1, 'Your operating system has support for the high performance kqueue socket engine. Would you like to enable it?', 1);
320                 if ($config{USE_KQUEUE}) {
321                         $config{SOCKETENGINE} = "kqueue";
322                         $chose_hiperf = 1;
323                 }
324         }
325         if ($config{HAS_EPOLL}) {
326                 $config{USE_EPOLL} = prompt_bool(1, 'Your operating system has support for the high performance epoll socket engine. Would you like to enable it?', 1);
327                 if ($config{USE_EPOLL}) {
328                         $config{SOCKETENGINE} = "epoll";
329                         $chose_hiperf = 1;
330                 }
331         }
332         if ($config{HAS_PORTS}) {
333                 $config{USE_PORTS} = prompt_bool(1, 'Your operating system has support for the high performance IOCP socket engine. Would you like to enable it?', 1);
334                 if ($config{USE_PORTS}) {
335                         $config{SOCKETENGINE} = "ports";
336                         $chose_hiperf = 1;
337                 }
338         }
339
340         if (!$chose_hiperf && $config{HAS_POLL}) {
341                 $config{USE_POLL} = prompt_bool(1, 'Your operating system has support for the mid performance poll socket engine. Would you like to enable it?', 1);
342                 if ($config{USE_POLL}) {
343                         $config{SOCKETENGINE} = "poll";
344                 }
345         }
346         unless ($chose_hiperf || $config{USE_POLL})
347         {
348                 print "No high-performance socket engines are available, or you chose not to enable one. Defaulting to select() engine.\n\n";
349                 $config{SOCKETENGINE} = "select";
350         }
351
352         if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y")
353         {
354                 print "Detected GnuTLS version: \e[1;32m" . $gnutls_ver . "\e[0m\n";
355                 print "Detected OpenSSL version: \e[1;32m" . $openssl_ver . "\e[0m\n\n";
356
357                 $config{USE_SSL} = prompt_bool(1, 'One or more SSL libraries detected. Would you like to enable SSL support?', 1);
358                 if ($config{USE_SSL})
359                 {
360                         if ($config{HAS_GNUTLS} eq "y")
361                         {
362                                 $config{USE_GNUTLS} = prompt_bool(1, 'Would you like to enable SSL with m_ssl_gnutls (recommended)?', 1);
363                                 if ($config{USE_GNUTLS})
364                                 {
365                                         print "Using GnuTLS SSL module.\n\n";
366                                         unlink 'src/modules/m_ssl_gnutls.cpp' if -f 'src/modules/m_ssl_gnutls.cpp';
367                                         symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!\n";
368                                 }
369                         }
370
371                         if ($config{HAS_OPENSSL} eq "y")
372                         {
373                                 $config{USE_OPENSSL} = prompt_bool(1, 'Would you like to enable SSL with m_ssl_openssl (recommended)?', 1);
374                                 if ($config{USE_OPENSSL})
375                                 {
376                                         print "Using OpenSSL SSL module.\n\n";
377                                         unlink 'src/modules/m_ssl_openssl.cpp' if -f 'src/modules/m_ssl_openssl.cpp';
378                                         symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!\n";
379                                 }
380                         }
381                 }
382         }
383         else
384         {
385                 print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed and\n";
386                 print "is in your path.\n\n";
387         }
388 }
389
390 # We are on a POSIX system, we can enable POSIX extras without asking
391 symlink "extra/m_regex_posix.cpp", "src/modules/m_regex_posix.cpp";
392
393 if (($config{USE_GNUTLS}) && ($config{HAS_GNUTLS} ne "y"))
394 {
395         print "Sorry, but i couldn't detect gnutls. Make sure pkg-config is in your path.\n";
396         exit 1;
397 }
398 if (($config{USE_OPENSSL}) && ($config{HAS_OPENSSL} ne "y"))
399 {
400         print "Sorry, but i couldn't detect openssl. Make sure pkg-config is in your path.\n";
401         exit 1;
402 }
403
404 if ($config{USE_GNUTLS} || $config{USE_OPENSSL}) {
405         if (my $val = prompt_bool($interactive, 'Would you like to generate SSL certificates now?', $interactive)) {
406                 unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem" && -r "$config{CONFIG_DIR}/dhparams.pem") {
407                         unless (system './tools/genssl auto') {
408                                 print "\nCertificate generation complete, copying to config directory... ";
409                                 File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n";
410                                 File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n";
411                                 File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n";
412                                 print "Done.\n\n";
413                         }
414                 } else {
415                         print "SSL Certificates found, skipping.\n\n"
416                 }
417         } else {
418                 print "Skipping SSL certificate generation in non-interactive mode.\n\n";
419         }
420 } else {
421         print "Skipping SSL Certificate generation, SSL support is not available.\n\n";
422 }
423
424 print "Writing \e[1;32m.config.cache\e[0m ...\n";
425 write_configure_cache(%config);
426 writefiles();
427 dump_hash();
428
429 print "\n";
430 print "To build your server with these settings, please run '\e[1;32mmake\e[0m' now.\n";
431 if ($config{USE_GNUTLS} || $config{USE_OPENSSL}) {
432         print "Please note: for \e[1;32mSSL support\e[0m you will need to load required\n";
433         print "modules in your config. This configure script has added those modules to the\n";
434         print "build process. For more info please refer to:\n";
435         print "\e[1;32mhttp://wiki.inspircd.org/Installation_From_Tarball\e[0m\n";
436 }
437 print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n";
438
439 sub writefiles {
440         chomp(my $incos = `uname -n -s -r`);
441         chomp(my $version = `sh src/version.sh`);
442         my $revision = get_revision();
443         my $branch = "InspIRCd-0.0";
444         if ($version =~ /^(InspIRCd-[0-9]+\.[0-9]+)\.[0-9]+/)
445         {
446                 $branch = $1;
447         }
448         print "Writing \e[1;32mconfig.h\e[0m\n";
449         open(FILEHANDLE, ">include/config.h.tmp");
450         print FILEHANDLE <<EOF;
451 /* Auto generated by configure, do not modify! */
452 #pragma once
453
454 #define BRANCH "$branch"
455 #define VERSION "$version"
456 #define REVISION "$revision"
457 #define SYSTEM "$incos"
458
459 #define CONFIG_PATH "$config{CONFIG_DIR}"
460 #define DATA_PATH "$config{DATA_DIR}"
461 #define LOG_PATH "$config{LOG_DIR}"
462 #define MOD_PATH "$config{MODULE_DIR}"
463
464 EOF
465
466         if ($config{HAS_EVENTFD}) {
467                 print FILEHANDLE "#define HAS_EVENTFD\n";
468         }
469         if ($config{HAS_CLOCK_GETTIME}) {
470                 print FILEHANDLE "#define HAS_CLOCK_GETTIME\n";
471         }
472
473         print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n";
474         close(FILEHANDLE);
475
476         unlink 'include/config.h';
477         rename 'include/config.h.tmp', 'include/config.h';
478
479         # Do this once here, and cache it in the .*.inc files,
480         # rather than attempting to read src/version.sh from
481         # compiled code -- we might not have the source to hand.
482
483         my @dotfiles = qw(main.mk inspircd);
484         push @dotfiles, 'org.inspircd.plist' if $^O eq 'darwin';
485
486         foreach my $file (@dotfiles) {
487                 open(FILEHANDLE, "make/template/$file") or die "Can't open make/template/$file: $!";
488                 $_ = join '', <FILEHANDLE>;
489                 close(FILEHANDLE);
490
491                 $config{BUILD_DIR} ||= rel2abs($topdir."/build");
492                 $config{COMPILER} = lc $cxx{NAME};
493                 $config{SYSTEM} = lc $^O;
494
495                 for my $var (qw(
496                         CXX COMPILER SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID
497                         STARTSCRIPT SOCKETENGINE
498                 )) {
499                         s/\@$var\@/$config{$var}/g;
500                 }
501
502                 s/\@VERSION\@/$version/ if defined $version;
503
504                 if ($file eq 'main.mk') {
505                         print "Writing \e[1;32mGNUmakefile\e[0m ...\n";
506
507                         my $mk_tmp = $_;
508                         s/\@IFDEF (\S+)/ifdef $1/g;
509                         s/\@IFNDEF (\S+)/ifndef $1/g;
510                         s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
511                         s/\@IFNEQ (\S+) (\S+)/ifneq ($1,$2)/g;
512                         s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
513                         s/\@ELSE/else/g;
514                         s/\@ENDIF/endif/g;
515                         s/ *\@BSD_ONLY .*\n//g;
516                         s/\@GNU_ONLY //g;
517                         s/\@DO_EXPORT (.*)/export $1/g;
518                         open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
519                         print MKF $_;
520                         close MKF;
521
522                         print "Writing \e[1;32mBSDmakefile\e[0m ...\n";
523                         $_ = $mk_tmp;
524                         s/\@IFDEF (\S+)/.if defined($1)/g;
525                         s/\@IFNDEF (\S+)/.if !defined($1)/g;
526                         s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g;
527                         s/\@IFNEQ (\S+) (\S+)/.if $1 != $2/g;
528                         s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g;
529                         s/\@ELSE/.else/g;
530                         s/\@ENDIF/.endif/g;
531                         s/\@BSD_ONLY //g;
532                         s/ *\@GNU_ONLY .*\n//g;
533                         $mk_tmp = $_;
534                         $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg;
535                         open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!";
536                         print MKF $mk_tmp;
537                         close MKF;
538                 } else {
539                         print "Writing \e[1;32m$file\e[0m ...\n";
540                         open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n");
541                         print FILEHANDLE $_;
542                         close(FILEHANDLE);
543                 }
544         }
545
546         chmod 0750, 'inspircd';
547 }
548
549 # Routine to list out the extra/ modules that have been enabled.
550 # Note: when getting any filenames out and comparing, it's important to lc it if the
551 # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
552 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
553 # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
554 sub list_extras () {
555         use File::Spec;
556         # @_ not used
557         my $srcdir = File::Spec->catdir("src", "modules");
558         my $abs_srcdir = File::Spec->rel2abs($srcdir);
559         local $_;
560         my $dd;
561         opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
562         my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
563         closedir $dd;
564         undef $dd;
565         opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
566         my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
567         closedir $dd;
568         undef $dd;
569         my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
570         my %extras = ();
571 EXTRA:  for my $extra (@extras) {
572                 next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
573                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
574                 my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
575                 next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
576                 if (-l $abs_source) {
577                         # Symlink, is it in the right place?
578                         my $targ = readlink($abs_source);
579                         my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
580                         if ($abs_targ eq $abs_extra) {
581                                 $extras{$extra} = "\e[32;1menabled\e[0m";
582                         } else {
583                                 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
584                         }
585                 } elsif (-e $abs_source) {
586                         my ($devext, $inoext) = stat($abs_extra);
587                         my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
588                         if ($lnksrc > 1) {
589                                 if ($devsrc == $devext && $inosrc == $inoext) {
590                                         $extras{$extra} = "\e[32;1menabled\e[0m";
591                                 } else {
592                                         $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
593                                 }
594                         } else {
595                                 open my $extfd, "<", $abs_extra;
596                                 open my $srcfd, "<", $abs_source;
597                                 local $/ = undef;
598                                 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
599                                         $extras{$extra} = "\e[32;1menabled\e[0m";
600                                 } else {
601                                         $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
602                                 }
603                         }
604                 } else {
605                         $extras{$extra} = "\e[33;1mdisabled\e[0m";
606                 }
607         }
608         # Now let's add dependency info
609         for my $extra (keys(%extras)) {
610                 next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
611                 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
612                 my @deps = split /\s+/, get_property($abs_extra, 'ModDep');
613                 for my $dep (@deps) {
614                         if (exists($extras{$dep})) {
615                                 my $ref = \$extras{$dep}; # Take reference.
616                                 if ($$ref !~ m/needed by/) {
617                                         # First dependency found.
618                                         if ($$ref =~ m/enabled/) {
619                                                 $$ref .= " (needed by \e[32;1m$extra\e[0m";
620                                         } else {
621                                                 $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later.
622                                                 $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m";
623                                         }
624                                 } else {
625                                         if ($$ref =~ m/enabled/) {
626                                                 $$ref .= ", \e[32;1m$extra\e[0m";
627                                         } else {
628                                                 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
629                                         }
630                                 }
631                         }
632                 }
633         }
634         for my $extra (sort {$a cmp $b} keys(%extras)) {
635                 my $text = $extras{$extra};
636                 if ($text =~ m/needed by/ && $text !~ m/enabled/) {
637                         printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
638                 } else {
639                         printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
640                 }
641         }
642         return keys(%extras) if wantarray; # Can be used by manage_extras.
643 }
644
645 sub enable_extras (@) {
646         my (@extras) = @_;
647         for my $extra (@extras) {
648                 my $extrapath = "src/modules/extra/$extra";
649                 if (!-e $extrapath) {
650                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
651                         next;
652                 }
653                 my $source = "src/modules/$extra";
654                 if (-e $source) {
655                         print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
656                         next;
657                 }
658                 # Get dependencies, and add them to be processed.
659                 my @deps = split /\s+/, get_property($extrapath, 'ModDep');
660                 for my $dep (@deps) {
661                         next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
662                         if (!-e "src/modules/$dep" && !-e "include/$dep") {
663                                 if (-e "src/modules/extra/$dep") {
664                                         print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
665                                         push @extras, $dep;
666                                 } else {
667                                         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";
668                                 }
669                         }
670                 }
671                 print "Enabling $extra ... \n";
672                 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
673         }
674 }
675
676 sub disable_extras (@)
677 {
678         opendir my $dd, "src/modules/extra/";
679         my @files = readdir($dd);
680         closedir $dd;
681         my (@extras) = @_;
682 EXTRA:  for my $extra (@extras) {
683                 my $extrapath = "src/modules/extra/$extra";
684                 my $source = "src/modules/$extra";
685                 if (!-e $extrapath) {
686                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
687                         next;
688                 }
689                 if ((! -l $source) || readlink($source) ne "extra/$extra") {
690                         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";
691                         next;
692                 }
693                 # Check if anything needs this.
694                 for my $file (@files) {
695                         my @deps = split /\s+/, get_property("src/modules/extra/$file", 'ModDep');
696                         # File depends on this extra...
697                         if (scalar(grep { $_ eq $extra } @deps) > 0) {
698                                 # And is both enabled and not about to be disabled.
699                                 if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
700                                         print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
701                                         next EXTRA;
702                                 }
703                         }
704                 }
705                 # Now remove.
706                 print "Disabling $extra ... \n";
707                 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";
708         }
709 }