4 # InspIRCd -- Internet Relay Chat Daemon
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>
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.
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
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/>.
34 use warnings FATAL => qw(all);
37 use File::Spec::Functions qw(rel2abs);
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);
50 sub enable_extras (@);
52 sub disable_extras (@);
55 my @opt_disableextras;
58 'enable-gnutls' => \$opt_use_gnutls,
59 'system' => \$opt_system,
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,
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, # ^
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);
84 print "Remember: YOU are responsible for making sure any libraries needed have been installed!\n";
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)
104 our $topdir = getcwd();
105 our %config = read_configure_cache();
107 print "Checking for cache from previous configure... ";
108 print %config ? "found\n" : "not found\n";
110 $config{BASE_DIR} = $topdir."/run";
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';
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';
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");
136 if (defined $opt_config_dir) {
137 $config{CONFIG_DIR} = $opt_config_dir;
139 if (defined $opt_module_dir) {
140 $config{MODULE_DIR} = $opt_module_dir;
142 if (defined $opt_binary_dir) {
143 $config{BINARY_DIR} = $opt_binary_dir;
145 if (defined $opt_data_dir) {
146 $config{DATA_DIR} = $opt_data_dir;
148 if (defined $opt_log_dir) {
149 $config{LOG_DIR} = $opt_log_dir;
151 chomp($config{HAS_GNUTLS} = `pkg-config --modversion gnutls 2>/dev/null`);
152 chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`);
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)
159 $config{USE_GNUTLS} = "y"; # Use gnutls.
161 $config{USE_OPENSSL} = 0; # Use openssl.
162 if (defined $opt_use_openssl)
164 $config{USE_OPENSSL} = "y";
167 $config{STARTSCRIPT} = $^O eq 'darwin' ? 'org.inspircd.plist' : 'inspircd';
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";
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";
183 if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)(?:[a-z])?(?:\-[a-z][0-9])?/) {
184 $config{HAS_OPENSSL} = $1;
186 $config{HAS_OPENSSL} = "";
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');
192 if ($config{HAS_EPOLL} = run_test 'epoll', test_header($config{CXX}, 'sys/epoll.h')) {
193 $config{SOCKETENGINE} ||= 'epoll';
196 if ($config{HAS_KQUEUE} = run_test 'kqueue', test_file($config{CXX}, 'kqueue.cpp')) {
197 $config{SOCKETENGINE} ||= 'kqueue';
200 if ($config{HAS_PORTS} = run_test 'Solaris IOCP', test_header($config{CXX}, 'port.h')) {
201 $config{SOCKETENGINE} ||= 'ports';
204 if ($config{HAS_POLL} = run_test 'poll', test_header($config{CXX}, 'poll.h')) {
205 $config{SOCKETENGINE} ||= 'poll';
208 # Select is available on all platforms
209 $config{HAS_SELECT} = 1;
210 $config{SOCKETENGINE} ||= "select";
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;
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};
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 "")) {
232 $config{HAS_GNUTLS} = "y";
235 $config{HAS_GNUTLS} = "n";
239 $config{HAS_GNUTLS} = "n";
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 "")) {
246 $config{HAS_OPENSSL} = "y";
249 $config{HAS_OPENSSL} = "n";
253 $config{HAS_OPENSSL} = "n";
259 system 'tput', 'clear';
261 my $revision = get_revision();
262 chomp(my $version = `sh src/version.sh`);
264 # Display Introduction Message..
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
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. ***
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!
277 Your operating system is: \e[1;32m$^O\e[0m
279 print "Your InspIRCd version is: \e[1;32m";
280 print $revision eq 'release' ? substr($version, 9) : substr($revision, 1);
282 print "The following compiler has been detected: \e[1;32m$cxx{NAME} $cxx{VERSION}\e[0m ($config{CXX})\n\n";
284 # Check that the user actually wants this version.
285 if (index($version, '+') != -1) {
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
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.
296 exit 1 unless prompt_bool(1, 'I understand this warning and want to continue anyway.', 0);
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");
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});
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";
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";
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";
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";
346 unless ($chose_hiperf || $config{USE_POLL})
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";
352 if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y")
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";
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})
360 if ($config{HAS_GNUTLS} eq "y")
362 $config{USE_GNUTLS} = prompt_bool(1, 'Would you like to enable SSL with m_ssl_gnutls (recommended)?', 1);
363 if ($config{USE_GNUTLS})
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";
371 if ($config{HAS_OPENSSL} eq "y")
373 $config{USE_OPENSSL} = prompt_bool(1, 'Would you like to enable SSL with m_ssl_openssl (recommended)?', 1);
374 if ($config{USE_OPENSSL})
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";
385 print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed and\n";
386 print "is in your path.\n\n";
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";
393 if (($config{USE_GNUTLS}) && ($config{HAS_GNUTLS} ne "y"))
395 print "Sorry, but i couldn't detect gnutls. Make sure pkg-config is in your path.\n";
398 if (($config{USE_OPENSSL}) && ($config{HAS_OPENSSL} ne "y"))
400 print "Sorry, but i couldn't detect openssl. Make sure pkg-config is in your path.\n";
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";
415 print "SSL Certificates found, skipping.\n\n"
418 print "Skipping SSL certificate generation in non-interactive mode.\n\n";
421 print "Skipping SSL Certificate generation, SSL support is not available.\n\n";
424 print "Writing \e[1;32m.config.cache\e[0m ...\n";
425 write_configure_cache(%config);
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";
437 print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n";
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]+/)
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! */
454 #define BRANCH "$branch"
455 #define VERSION "$version"
456 #define REVISION "$revision"
457 #define SYSTEM "$incos"
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}"
466 if ($config{HAS_EVENTFD}) {
467 print FILEHANDLE "#define HAS_EVENTFD\n";
469 if ($config{HAS_CLOCK_GETTIME}) {
470 print FILEHANDLE "#define HAS_CLOCK_GETTIME\n";
473 print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n";
476 unlink 'include/config.h';
477 rename 'include/config.h.tmp', 'include/config.h';
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.
483 my @dotfiles = qw(main.mk inspircd);
484 push @dotfiles, 'org.inspircd.plist' if $^O eq 'darwin';
486 foreach my $file (@dotfiles) {
487 open(FILEHANDLE, "make/template/$file") or die "Can't open make/template/$file: $!";
488 $_ = join '', <FILEHANDLE>;
491 $config{BUILD_DIR} ||= rel2abs($topdir."/build");
492 $config{COMPILER} = lc $cxx{NAME};
493 $config{SYSTEM} = lc $^O;
496 CXX COMPILER SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID
497 STARTSCRIPT SOCKETENGINE
499 s/\@$var\@/$config{$var}/g;
502 s/\@VERSION\@/$version/ if defined $version;
504 if ($file eq 'main.mk') {
505 print "Writing \e[1;32mGNUmakefile\e[0m ...\n";
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;
515 s/ *\@BSD_ONLY .*\n//g;
517 s/\@DO_EXPORT (.*)/export $1/g;
518 open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
522 print "Writing \e[1;32mBSDmakefile\e[0m ...\n";
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;
532 s/ *\@GNU_ONLY .*\n//g;
534 $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg;
535 open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!";
539 print "Writing \e[1;32m$file\e[0m ...\n";
540 open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n");
546 chmod 0750, 'inspircd';
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).
557 my $srcdir = File::Spec->catdir("src", "modules");
558 my $abs_srcdir = File::Spec->rel2abs($srcdir);
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));
565 opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
566 my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
569 my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
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";
583 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
585 } elsif (-e $abs_source) {
586 my ($devext, $inoext) = stat($abs_extra);
587 my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
589 if ($devsrc == $devext && $inosrc == $inoext) {
590 $extras{$extra} = "\e[32;1menabled\e[0m";
592 $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
595 open my $extfd, "<", $abs_extra;
596 open my $srcfd, "<", $abs_source;
598 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
599 $extras{$extra} = "\e[32;1menabled\e[0m";
601 $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
605 $extras{$extra} = "\e[33;1mdisabled\e[0m";
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";
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";
625 if ($$ref =~ m/enabled/) {
626 $$ref .= ", \e[32;1m$extra\e[0m";
628 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
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/ ? ")" : "");
639 printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
642 return keys(%extras) if wantarray; # Can be used by manage_extras.
645 sub enable_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";
653 my $source = "src/modules/$extra";
655 print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
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";
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";
671 print "Enabling $extra ... \n";
672 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
676 sub disable_extras (@)
678 opendir my $dd, "src/modules/extra/";
679 my @files = readdir($dd);
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";
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";
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";
706 print "Disabling $extra ... \n";
707 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";