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"
458 #define INSPIRCD_SOCKETENGINE_NAME "$config{SOCKETENGINE}"
460 #define CONFIG_PATH "$config{CONFIG_DIR}"
461 #define DATA_PATH "$config{DATA_DIR}"
462 #define LOG_PATH "$config{LOG_DIR}"
463 #define MOD_PATH "$config{MODULE_DIR}"
467 if ($config{HAS_EVENTFD}) {
468 print FILEHANDLE "#define HAS_EVENTFD\n";
470 if ($config{HAS_CLOCK_GETTIME}) {
471 print FILEHANDLE "#define HAS_CLOCK_GETTIME\n";
474 print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n";
477 unlink 'include/config.h';
478 rename 'include/config.h.tmp', 'include/config.h';
480 # Do this once here, and cache it in the .*.inc files,
481 # rather than attempting to read src/version.sh from
482 # compiled code -- we might not have the source to hand.
484 my @dotfiles = qw(main.mk inspircd);
485 push @dotfiles, 'org.inspircd.plist' if $^O eq 'darwin';
487 foreach my $file (@dotfiles) {
488 open(FILEHANDLE, "make/template/$file") or die "Can't open make/template/$file: $!";
489 $_ = join '', <FILEHANDLE>;
492 $config{BUILD_DIR} ||= rel2abs($topdir."/build");
493 $config{COMPILER} = lc $cxx{NAME};
494 $config{SYSTEM} = lc $^O;
497 CXX COMPILER SYSTEM BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR BUILD_DIR DATA_DIR UID
498 STARTSCRIPT SOCKETENGINE
500 s/\@$var\@/$config{$var}/g;
503 s/\@VERSION\@/$version/ if defined $version;
505 if ($file eq 'main.mk') {
506 print "Writing \e[1;32mGNUmakefile\e[0m ...\n";
509 s/\@IFDEF (\S+)/ifdef $1/g;
510 s/\@IFNDEF (\S+)/ifndef $1/g;
511 s/\@IFEQ (\S+) (\S+)/ifeq ($1,$2)/g;
512 s/\@IFNEQ (\S+) (\S+)/ifneq ($1,$2)/g;
513 s/\@ELSIFEQ (\S+) (\S+)/else ifeq ($1,$2)/g;
516 s/ *\@BSD_ONLY .*\n//g;
518 s/\@DO_EXPORT (.*)/export $1/g;
519 open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!";
523 print "Writing \e[1;32mBSDmakefile\e[0m ...\n";
525 s/\@IFDEF (\S+)/.if defined($1)/g;
526 s/\@IFNDEF (\S+)/.if !defined($1)/g;
527 s/\@IFEQ (\S+) (\S+)/.if $1 == $2/g;
528 s/\@IFNEQ (\S+) (\S+)/.if $1 != $2/g;
529 s/\@ELSIFEQ (\S+) (\S+)/.elif $1 == $2/g;
533 s/ *\@GNU_ONLY .*\n//g;
535 $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg;
536 open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!";
540 print "Writing \e[1;32m$file\e[0m ...\n";
541 open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n");
547 chmod 0750, 'inspircd';
550 # Routine to list out the extra/ modules that have been enabled.
551 # Note: when getting any filenames out and comparing, it's important to lc it if the
552 # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32
553 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on
554 # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior).
558 my $srcdir = File::Spec->catdir("src", "modules");
559 my $abs_srcdir = File::Spec->rel2abs($srcdir);
562 opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n");
563 my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
566 opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n";
567 my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
570 my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
572 EXTRA: for my $extra (@extras) {
573 next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
574 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
575 my $abs_source = File::Spec->catfile($abs_srcdir, $extra);
576 next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory
577 if (-l $abs_source) {
578 # Symlink, is it in the right place?
579 my $targ = readlink($abs_source);
580 my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir);
581 if ($abs_targ eq $abs_extra) {
582 $extras{$extra} = "\e[32;1menabled\e[0m";
584 $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ);
586 } elsif (-e $abs_source) {
587 my ($devext, $inoext) = stat($abs_extra);
588 my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source);
590 if ($devsrc == $devext && $inosrc == $inoext) {
591 $extras{$extra} = "\e[32;1menabled\e[0m";
593 $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc);
596 open my $extfd, "<", $abs_extra;
597 open my $srcfd, "<", $abs_source;
599 if (scalar(<$extfd>) eq scalar(<$srcfd>)) {
600 $extras{$extra} = "\e[32;1menabled\e[0m";
602 $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m");
606 $extras{$extra} = "\e[33;1mdisabled\e[0m";
609 # Now let's add dependency info
610 for my $extra (keys(%extras)) {
611 next unless $extras{$extra} =~ m/enabled/; # only process enabled extras.
612 my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra);
613 my @deps = split /\s+/, get_property($abs_extra, 'ModDep');
614 for my $dep (@deps) {
615 if (exists($extras{$dep})) {
616 my $ref = \$extras{$dep}; # Take reference.
617 if ($$ref !~ m/needed by/) {
618 # First dependency found.
619 if ($$ref =~ m/enabled/) {
620 $$ref .= " (needed by \e[32;1m$extra\e[0m";
622 $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later.
623 $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m";
626 if ($$ref =~ m/enabled/) {
627 $$ref .= ", \e[32;1m$extra\e[0m";
629 $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m";
635 for my $extra (sort {$a cmp $b} keys(%extras)) {
636 my $text = $extras{$extra};
637 if ($text =~ m/needed by/ && $text !~ m/enabled/) {
638 printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
640 printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
643 return keys(%extras) if wantarray; # Can be used by manage_extras.
646 sub enable_extras (@) {
648 for my $extra (@extras) {
649 my $extrapath = "src/modules/extra/$extra";
650 if (!-e $extrapath) {
651 print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
654 my $source = "src/modules/$extra";
656 print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
659 # Get dependencies, and add them to be processed.
660 my @deps = split /\s+/, get_property($extrapath, 'ModDep');
661 for my $dep (@deps) {
662 next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
663 if (!-e "src/modules/$dep" && !-e "include/$dep") {
664 if (-e "src/modules/extra/$dep") {
665 print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
668 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";
672 print "Enabling $extra ... \n";
673 symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
677 sub disable_extras (@)
679 opendir my $dd, "src/modules/extra/";
680 my @files = readdir($dd);
683 EXTRA: for my $extra (@extras) {
684 my $extrapath = "src/modules/extra/$extra";
685 my $source = "src/modules/$extra";
686 if (!-e $extrapath) {
687 print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
690 if ((! -l $source) || readlink($source) ne "extra/$extra") {
691 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";
694 # Check if anything needs this.
695 for my $file (@files) {
696 my @deps = split /\s+/, get_property("src/modules/extra/$file", 'ModDep');
697 # File depends on this extra...
698 if (scalar(grep { $_ eq $extra } @deps) > 0) {
699 # And is both enabled and not about to be disabled.
700 if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
701 print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
707 print "Disabling $extra ... \n";
708 unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";