]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - configure
Sync helpop chmodes s and p with docs
[user/henk/code/inspircd.git] / configure
index a9d02ffc449c793bb0c7f1e958f1c580c368d7c2..f1813bf09801322db0617e615f851d12aee474c6 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,17 +1,18 @@
 #!/usr/bin/env perl
-
 #
 # InspIRCd -- Internet Relay Chat Daemon
 #
-#   Copyright (C) 2012-2017 Peter Powell <petpow@saberuk.com>
-#   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
-#   Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
-#   Copyright (C) 2003, 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
-#   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
+#   Copyright (C) 2020 Nicole Kleinhoff <ilbelkyr@shalture.org>
+#   Copyright (C) 2020 Daniel Vassdal <shutter@canternet.org>
+#   Copyright (C) 2019 Matt Schatz <genius3000@g3k.solutions>
+#   Copyright (C) 2013-2021 Sadie Powell <sadie@witchery.services>
+#   Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be>
+#   Copyright (C) 2012 ChrisTX <xpipe@hotmail.de>
+#   Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.org>
 #   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
-#   Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
-#   Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
-#   Copyright (C) 2003-2006 Craig McLure <craig@chatspike.net>
+#   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+#   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+#   Copyright (C) 2006-2008 Craig Edwards <brain@inspircd.org>
 #
 # This file is part of InspIRCd.  InspIRCd is free software: you can
 # redistribute it and/or modify it under the terms of the GNU General Public
 #
 
 
-BEGIN {
-       require 5.10.0;
-}
-
-use feature ':5.10';
+use v5.10.0;
 use strict;
 use warnings FATAL => qw(all);
 
+use Cwd                   qw(getcwd);
 use File::Basename        qw(basename);
 use File::Copy            ();
-use File::Spec::Functions qw(rel2abs);
+use File::Spec::Functions qw(abs2rel catfile catdir rel2abs);
 use FindBin               qw($RealDir);
 use Getopt::Long          qw(GetOptions);
 use POSIX                 qw(getgid getuid);
@@ -52,13 +50,18 @@ my ($opt_binary_dir,
     $opt_config_dir,
     $opt_data_dir,
     $opt_development,
+    $opt_disable_auto_extras,
     $opt_disable_interactive,
+    $opt_disable_ownership,
     $opt_distribution_label,
+    $opt_example_dir,
     $opt_gid,
     $opt_log_dir,
     $opt_manual_dir,
     $opt_module_dir,
+    $opt_portable,
     $opt_prefix,
+    $opt_runtime_dir,
     $opt_script_dir,
     $opt_socketengine,
     $opt_system,
@@ -73,36 +76,40 @@ sub disable_extras (@);
 my @opt_enableextras;
 my @opt_disableextras;
 
-GetOptions(
+exit 1 unless GetOptions(
        'clean'  => \&cmd_clean,
        'help'   => \&cmd_help,
        'update' => \&cmd_update,
 
-       'development'          => \$opt_development,
-       'disable-interactive'  => \$opt_disable_interactive,
-       'distribution-label=s' => \$opt_distribution_label,
        'binary-dir=s'         => \$opt_binary_dir,
        'config-dir=s'         => \$opt_config_dir,
        'data-dir=s'           => \$opt_data_dir,
+       'development'          => \$opt_development,
+       'disable-auto-extras'  => \$opt_disable_auto_extras,
+       'disable-interactive'  => \$opt_disable_interactive,
+       'disable-ownership'    => \$opt_disable_ownership,
+       'distribution-label=s' => \$opt_distribution_label,
+       'example-dir=s'        => \$opt_example_dir,
        'gid=s'                => \$opt_gid,
        'log-dir=s'            => \$opt_log_dir,
        'manual-dir=s'         => \$opt_manual_dir,
        'module-dir=s'         => \$opt_module_dir,
+       'portable'             => \$opt_portable,
        'prefix=s'             => \$opt_prefix,
+       'runtime-dir=s'        => \$opt_runtime_dir,
        'script-dir=s'         => \$opt_script_dir,
        'socketengine=s'       => \$opt_socketengine,
        'system'               => \$opt_system,
        'uid=s'                => \$opt_uid,
 
-       # TODO: when the modulemanager rewrite is done these should be removed.
        'disable-extras=s@' => \@opt_disableextras,
        'enable-extras=s@'  => \@opt_enableextras,
        'list-extras'       => sub { list_extras; exit 0; },
 );
 
 if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) {
-       @opt_enableextras = split /,/, join(',', @opt_enableextras);
-       @opt_disableextras = split /,/, join(',', @opt_disableextras);
+       @opt_enableextras = grep { /\S/ } split /[, ]+/, join(',', @opt_enableextras);
+       @opt_disableextras = grep { /\S/ } split /[, ]+/, join(',', @opt_disableextras);
        enable_extras(@opt_enableextras);
        disable_extras(@opt_disableextras);
        list_extras;
@@ -117,13 +124,18 @@ our $interactive = !(
        defined $opt_config_dir ||
        defined $opt_data_dir ||
        defined $opt_development ||
+       defined $opt_disable_auto_extras ||
        defined $opt_disable_interactive ||
+       defined $opt_disable_ownership ||
        defined $opt_distribution_label ||
+       defined $opt_example_dir ||
        defined $opt_gid ||
        defined $opt_log_dir ||
        defined $opt_manual_dir ||
        defined $opt_module_dir ||
+       defined $opt_portable ||
        defined $opt_prefix ||
+       defined $opt_runtime_dir ||
        defined $opt_script_dir ||
        defined $opt_socketengine ||
        defined $opt_system ||
@@ -131,12 +143,12 @@ our $interactive = !(
 );
 
 my %version = get_version $opt_distribution_label;
-print_format "<|BOLD Configuring InspIRCd $version{FULL} on $^O.|>\n";
+say console_format "<|BOLD Configuring InspIRCd $version{FULL} on $^O.|>";
 
 my %config;
 if ($interactive) {
        %config = read_config_file(CONFIGURE_CACHE_FILE);
-       run_test CONFIGURE_CACHE_FILE, %config;
+       run_test abs2rel(CONFIGURE_CACHE_FILE, $RealDir), %config;
        if (!defined $config{VERSION}) {
                $config{VERSION} = CONFIGURE_CACHE_VERSION;
        } elsif ($config{VERSION} != CONFIGURE_CACHE_VERSION) {
@@ -179,29 +191,49 @@ if (defined $opt_socketengine) {
 }
 $config{SOCKETENGINE} = $opt_socketengine // $socketengines[0];
 
-if (defined $opt_system) {
-       $config{BASE_DIR}   = $opt_prefix     // '/var/lib/inspircd';
-       $config{BINARY_DIR} = $opt_binary_dir // '/usr/sbin';
-       $config{CONFIG_DIR} = $opt_config_dir // '/etc/inspircd';
-       $config{DATA_DIR}   = $opt_data_dir   // '/var/inspircd';
-       $config{LOG_DIR}    = $opt_module_dir // '/var/log/inspircd';
-       $config{MANUAL_DIR} = $opt_manual_dir // '/usr/share/man/man1';
-       $config{MODULE_DIR} = $opt_module_dir // '/usr/lib/inspircd';
-       $config{SCRIPT_DIR} = $opt_script_dir // '/usr/share/inspircd'
+if (defined $opt_portable) {
+       print_error '--portable and --system can not be used together!' if defined $opt_system;
+       $config{DESTDIR}     = catfile $RealDir, 'run', '';
+       $config{BASE_DIR}    = $opt_prefix      // '';
+       $config{BINARY_DIR}  = $opt_binary_dir  // 'bin';
+       $config{CONFIG_DIR}  = $opt_config_dir  // 'conf';
+       $config{DATA_DIR}    = $opt_data_dir    // 'data';
+       $config{EXAMPLE_DIR} = $opt_example_dir // catdir $config{CONFIG_DIR}, 'examples';
+       $config{LOG_DIR}     = $opt_log_dir     // 'logs';
+       $config{MANUAL_DIR}  = $opt_manual_dir  // 'manuals';
+       $config{MODULE_DIR}  = $opt_module_dir  // 'modules';
+       $config{RUNTIME_DIR} = $opt_runtime_dir // $config{DATA_DIR};
+       $config{SCRIPT_DIR}  = $opt_script_dir  // $config{BASE_DIR};
+} elsif (defined $opt_system) {
+       $config{BASE_DIR}    = $opt_prefix      // '/';
+       $config{BINARY_DIR}  = $opt_binary_dir  // catdir $config{BASE_DIR}, 'usr/sbin';
+       $config{CONFIG_DIR}  = $opt_config_dir  // catdir $config{BASE_DIR}, 'etc/inspircd';
+       $config{DATA_DIR}    = $opt_data_dir    // catdir $config{BASE_DIR}, 'var/lib/inspircd';
+       $config{EXAMPLE_DIR} = $opt_example_dir // catdir $config{BASE_DIR}, 'usr/share/doc/inspircd';
+       $config{LOG_DIR}     = $opt_log_dir     // catdir $config{BASE_DIR}, 'var/log/inspircd';
+       $config{MANUAL_DIR}  = $opt_manual_dir  // catdir $config{BASE_DIR}, 'usr/share/man/man1';
+       $config{MODULE_DIR}  = $opt_module_dir  // catdir $config{BASE_DIR}, 'usr/lib/inspircd';
+       $config{RUNTIME_DIR} = $opt_runtime_dir // catdir $config{BASE_DIR}, $^O eq 'linux' ? '/run/inspircd' : '/var/run/inspircd';
+       $config{SCRIPT_DIR}  = $opt_script_dir  // catdir $config{BASE_DIR}, 'usr/share/inspircd';
 } else {
-       $config{BASE_DIR}   = $opt_prefix     // $config{BASE_DIR}   // rel2abs 'run';
-       $config{BINARY_DIR} = $opt_binary_dir // $config{BINARY_DIR} // rel2abs $config{BASE_DIR} . '/bin';
-       $config{CONFIG_DIR} = $opt_config_dir // $config{CONFIG_DIR} // rel2abs $config{BASE_DIR} . '/conf';
-       $config{DATA_DIR}   = $opt_data_dir   // $config{DATA_DIR}   // rel2abs $config{BASE_DIR} . '/data';
-       $config{LOG_DIR}    = $opt_log_dir    // $config{LOG_DIR}    // rel2abs $config{BASE_DIR} . '/logs';
-       $config{MANUAL_DIR} = $opt_manual_dir // $config{MANUAL_DIR} // rel2abs $config{BASE_DIR} . '/manuals';
-       $config{MODULE_DIR} = $opt_module_dir // $config{MODULE_DIR} // rel2abs $config{BASE_DIR} . '/modules';
-       $config{SCRIPT_DIR} = $opt_script_dir // $config{SCRIPT_DIR} // $config{BASE_DIR};
+       $config{BASE_DIR}    = rel2abs $opt_prefix // $config{BASE_DIR}    // catdir $RealDir,            'run';
+       $config{BINARY_DIR}  = $opt_binary_dir     // $config{BINARY_DIR}  // catdir $config{BASE_DIR},   'bin';
+       $config{CONFIG_DIR}  = $opt_config_dir     // $config{CONFIG_DIR}  // catdir $config{BASE_DIR},   'conf';
+       $config{DATA_DIR}    = $opt_data_dir       // $config{DATA_DIR}    // catdir $config{BASE_DIR},   'data';
+       $config{EXAMPLE_DIR} = $opt_example_dir    // $config{EXAMPLE_DIR} // catdir $config{CONFIG_DIR}, 'examples';
+       $config{LOG_DIR}     = $opt_log_dir        // $config{LOG_DIR}     // catdir $config{BASE_DIR},   'logs';
+       $config{MANUAL_DIR}  = $opt_manual_dir     // $config{MANUAL_DIR}  // catdir $config{BASE_DIR},   'manuals';
+       $config{MODULE_DIR}  = $opt_module_dir     // $config{MODULE_DIR}  // catdir $config{BASE_DIR},   'modules';
+       $config{RUNTIME_DIR} = $opt_runtime_dir    // $config{RUNTIME_DIR} // $config{DATA_DIR};
+       $config{SCRIPT_DIR}  = $opt_script_dir     // $config{SCRIPT_DIR}  // $config{BASE_DIR};
 }
 
 # Parse --gid=123 or --gid=foo and extract the group id.
 my @group;
-if (defined $opt_gid) {
+if (defined $opt_disable_ownership) {
+       @group = qw(insert-group-here . -1);
+       print_error 'you can not use --disable-ownership and --gid at the same time!' if defined $opt_gid;
+} elsif (defined $opt_gid) {
        @group = $opt_gid =~ /^\d+$/ ? getgrgid($opt_gid) : getgrnam($opt_gid);
        print_error "there is no '$opt_gid' group on this system!" unless @group;
 } else {
@@ -217,6 +249,7 @@ unprivileged user/group to build and run as or pass the '--gid [id|name]' flag
 to specify an unprivileged group to run as.
 EOW
                if (!prompt_bool $interactive, "Are you sure you want to build as the $group[0] group?", 0) {
+                       # PACKAGERS: You do not need to delete this check. Use `--disable-ownership` instead.
                        say STDERR "If you are sure you want to build as the $group[0] group pass the --gid $group[2] flag." unless $interactive;
                        exit 1;
                }
@@ -227,7 +260,10 @@ $config{GID}   = $group[2];
 
 # Parse --uid=123 or --uid=foo and extract the user id.
 my @user;
-if (defined $opt_uid) {
+if (defined $opt_disable_ownership) {
+       @user = qw(insert-user-here . -1);
+       print_error 'you can not use --disable-ownership and --uid at the same time!' if defined $opt_uid;
+} elsif (defined $opt_uid) {
        @user = $opt_uid =~ /^\d+$/ ? getpwuid($opt_uid) : getpwnam($opt_uid);
        print_error "there is no '$opt_uid' user on this system!" unless @user;
 } else {
@@ -243,6 +279,7 @@ unprivileged user/group to build and run as or pass the '--uid [id|name]' flag
 to specify an unprivileged user to run as.
 EOW
                if (!prompt_bool $interactive, "Are you sure you want to build as the $user[0] user?", 0) {
+                       # PACKAGERS: You do not need to delete this check. Use `--disable-ownership` instead.
                        say STDERR "If you are sure you want to build as the $user[0] user pass the --uid $user[2] flag." unless $interactive;
                        exit 1;
                }
@@ -260,8 +297,40 @@ configured on the Hardware Node. Failure to do so may result in clock drifting!
 EOW
 }
 
+# Warn the user about OpenBSD shipping incredibly broken compilers/linkers.
+if ($^O eq 'openbsd') {
+       print_warning <<'EOW';
+You are building InspIRCd on OpenBSD. The C++ compilers and linkers
+that OpenBSD ship are incredibly broken. You may have strange linker errors
+and crashes. Please consider using a different OS like FreeBSD/NetBSD instead.
+EOW
+}
+
+# Warn about Perl versions that will not be supported in the future.
+if ($^V lt 'v5.26.0') {
+       print_warning <<"EOW";
+You are building InspIRCd with Perl $^V. This is very old and will
+not be supported by the next major version of InspIRCd. Please consider updating
+to Perl v5.26 or newer.
+EOW
+}
+
+# Warn about compiler versions that will not be supported in the future.
+my %future_compilers = (
+       AppleClang => version->parse('10.0'),
+       Clang      => version->parse('5.0'),
+       GCC        => version->parse('7.0'),
+);
+if (exists $future_compilers{$compiler{NAME}} && $compiler{VERSION} lt $future_compilers{$compiler{NAME}}) {
+       print_warning <<"EOW";
+You are building InspIRCd with $compiler{NAME} v$compiler{VERSION}. This is very old and
+will not be supported by the next major version of InspIRCd. Please consider
+updating to $compiler{NAME} v$future_compilers{$compiler{NAME}} or newer.
+EOW
+}
+
 # Check that the user actually wants this version.
-if ($version{LABEL} ne 'release') {
+if (defined $version{REAL_LABEL}) {
        print_warning <<'EOW';
 You are building a development version. This contains code which has
 not been tested as heavily and may contain various faults which could seriously
@@ -282,7 +351,6 @@ EOW
 my $question = <<"EOQ";
 Currently, InspIRCd is configured with the following paths:
 
-<|BOLD Base:|>   $config{BASE_DIR}
 <|BOLD Binary:|> $config{BINARY_DIR}
 <|BOLD Config:|> $config{CONFIG_DIR}
 <|BOLD Data:|>   $config{DATA_DIR}
@@ -296,7 +364,7 @@ EOQ
 if (prompt_bool $interactive, $question, 0) {
        my $original_base_dir = $config{BASE_DIR};
        $config{BASE_DIR} = prompt_dir $interactive, 'In what directory do you wish to install the InspIRCd base?', $config{BASE_DIR};
-       foreach my $key (qw(BINARY_DIR CONFIG_DIR DATA_DIR LOG_DIR MANUAL_DIR MODULE_DIR SCRIPT_DIR)) {
+       for my $key (qw(BINARY_DIR CONFIG_DIR DATA_DIR LOG_DIR MANUAL_DIR MODULE_DIR SCRIPT_DIR)) {
                $config{$key} =~ s/^\Q$original_base_dir\E/$config{BASE_DIR}/;
        }
        $config{BINARY_DIR} = prompt_dir $interactive, 'In what directory should the InspIRCd binary be placed?', $config{BINARY_DIR};
@@ -306,6 +374,8 @@ if (prompt_bool $interactive, $question, 0) {
        $config{MANUAL_DIR} = prompt_dir $interactive, 'In what directory are manual pages to be placed?',        $config{MANUAL_DIR};
        $config{MODULE_DIR} = prompt_dir $interactive, 'In what directory are modules to be placed?',             $config{MODULE_DIR};
        $config{SCRIPT_DIR} = prompt_dir $interactive, 'In what directory are scripts to be placed?',             $config{SCRIPT_DIR};
+       $config{EXAMPLE_DIR} = $config{CONFIG_DIR} . '/examples';
+       $config{RUNTIME_DIR} = $config{DATA_DIR};
 }
 
 # Configure module settings.
@@ -315,26 +385,27 @@ Currently, InspIRCd is configured to automatically enable all available extra mo
 Would you like to enable extra modules manually?
 EOQ
 if (prompt_bool $interactive, $question, 0) {
-       foreach my $extra (<src/modules/extra/m_*.cpp>) {
-               my $module_name = basename $extra, '.cpp';
-               if (prompt_bool $interactive, "Would you like to enable $module_name?", 0) {
-                       enable_extras "$module_name.cpp";
+       for my $extra (<$RealDir/src/modules/extra/m_*.cpp>) {
+               my $module_name = module_shrink $extra;
+               if (prompt_bool $interactive, "Would you like to enable the <|BOLD $module_name|> module?", 0) {
+                       enable_extras $module_name;
                }
        }
-} else {
-       # TODO: finish modulemanager rewrite and replace this code with:
-       # system './modulemanager', 'enable', '--auto';
+} elsif (!defined $opt_disable_auto_extras) {
        my %modules = (
-               # Missing: m_ldap, m_regex_stdlib, m_ssl_mbedtls
+               'm_argon2.cpp'          => 'pkg-config --exists libargon2',
                'm_geo_maxmind.cpp'     => 'pkg-config --exists libmaxminddb',
                'm_mysql.cpp'           => 'mysql_config --version',
                'm_pgsql.cpp'           => 'pg_config --version',
+               'm_ldap.cpp'            => "echo '#include <ldap.h>' | $config{CXX} -E -",
                'm_regex_pcre.cpp'      => 'pcre-config --version',
                'm_regex_posix.cpp'     => undef,
                'm_regex_re2.cpp'       => 'pkg-config --exists re2',
+               'm_regex_stdlib.cpp'    => "$config{CXX} -o /dev/null -std=c++11 $RealDir/make/test/compiler.cpp",
                'm_regex_tre.cpp'       => 'pkg-config --exists tre',
                'm_sqlite3.cpp'         => 'pkg-config --exists sqlite3',
                'm_ssl_gnutls.cpp'      => 'pkg-config --exists gnutls',
+               'm_ssl_mbedtls.cpp'     => "echo '#include <mbedtls/version.h>' | $config{CXX} -E -",
                'm_ssl_openssl.cpp'     => 'pkg-config --exists openssl',
                'm_sslrehashsignal.cpp' => undef,
        );
@@ -354,17 +425,44 @@ Note: you can get a <|BOLD free|> CA-signed certificate from Let's Encrypt. See
 https://letsencrypt.org/getting-started/ for more details.
 EOQ
 
-if (<src/modules/m_ssl_*.cpp> && prompt_bool $interactive, $question, $interactive) {
-       system './tools/genssl', 'auto';
+if (<$RealDir/src/modules/m_ssl_*.cpp>) {
+       if (prompt_bool $interactive, $question, $interactive) {
+               create_directory CONFIGURE_DIRECTORY, 0750 or print_error "unable to create ${\CONFIGURE_DIRECTORY}: $!";
+               system './tools/genssl', 'auto', CONFIGURE_DIRECTORY;
+       } else {
+               my @pems = <${\CONFIGURE_DIRECTORY}/{cert,csr,dhparams,key}.pem>;
+               $question = <<EOQ;
+The following self-signed files were previously generated and will be installed
+when you run Make. Do you want to delete them?
+
+  * ${\join "\n  * ", @pems}
+EOQ
+               if (@pems && prompt_bool $interactive, $question, 0) {
+                       unlink @pems;
+               }
+       }
+} elsif (!defined $opt_disable_auto_extras) {
+       print_warning <<"EOM";
+you are building without enabling any SSL modules. This is not
+recommended as SSL greatly enhances the security and privacy of your IRC server
+and in a future version will be <|BOLD required|> for linking servers.
+
+Please read the following documentation pages on how to enable SSL support:
+
+GnuTLS (recommended): https://docs.inspircd.org/3/modules/ssl_gnutls
+mbedTLS:              https://docs.inspircd.org/3/modules/ssl_mbedtls
+OpenSSL:              https://docs.inspircd.org/3/modules/ssl_openssl
+EOM
 }
 
 # Cache the distribution label so that its not lost when --update is run.
 $config{DISTRIBUTION} = $opt_distribution_label if $opt_distribution_label;
+$config{DISABLE_OWNERSHIP} = $opt_disable_ownership // 0;
 
 write_configure_cache %config;
 parse_templates \%config, \%compiler, \%version;
 
-print_format <<"EOM";
+print console_format <<"EOM";
 
 Configuration is complete! You have chosen to build with the following settings:
 
@@ -376,28 +474,32 @@ Configuration is complete! You have chosen to build with the following settings:
 <|GREEN Extra Modules:|>
 EOM
 
-for my $file (<src/modules/m_*>) {
-       my $module = basename $file, '.cpp';
-       say "  * $module" if -l $file;
+for my $file (<$RealDir/src/modules/m_*>) {
+       say "  * ${\module_shrink $file}" if -l $file;
 }
 
-print_format <<"EOM";
+my @makeargs;
+push @makeargs, "-C${\abs2rel $RealDir}" unless getcwd eq $RealDir;
+push @makeargs, "-j${\(get_cpu_count() + 1)}";
+
+print console_format <<"EOM";
 
 <|GREEN Paths:|>
-  <|GREEN Base:|>   $config{BASE_DIR}
-  <|GREEN Binary:|> $config{BINARY_DIR}
-  <|GREEN Config:|> $config{CONFIG_DIR}
-  <|GREEN Data:|>   $config{DATA_DIR}
-  <|GREEN Log:|>    $config{LOG_DIR}
-  <|GREEN Manual:|> $config{MANUAL_DIR}
-  <|GREEN Module:|> $config{MODULE_DIR}
-  <|GREEN Script:|> $config{SCRIPT_DIR}
+  <|GREEN Binary:|>  $config{BINARY_DIR}
+  <|GREEN Config:|>  $config{CONFIG_DIR}
+  <|GREEN Data:|>    $config{DATA_DIR}
+  <|GREEN Example:|> $config{EXAMPLE_DIR}
+  <|GREEN Log:|>     $config{LOG_DIR}
+  <|GREEN Manual:|>  $config{MANUAL_DIR}
+  <|GREEN Module:|>  $config{MODULE_DIR}
+  <|GREEN Runtime:|> $config{RUNTIME_DIR}
+  <|GREEN Script:|>  $config{SCRIPT_DIR}
 
 <|GREEN Execution Group:|> $config{GROUP} ($config{GID})
 <|GREEN Execution User:|>  $config{USER} ($config{UID})
 <|GREEN Socket Engine:|>   $config{SOCKETENGINE}
 
-To build with these settings run '<|GREEN make -j${\get_cpu_count} install|>' now.
+To build with these settings run '<|GREEN make ${\join ' ', @makeargs} install|>' now.
 
 EOM
 
@@ -421,7 +523,7 @@ sub list_extras () {
        my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd));
        closedir $dd;
        undef $dd;
-       my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0];
+       my $maxlen = (sort { $b <=> $a } (map { length module_shrink $_ } (@extras)))[0];
        my %extras = ();
 EXTRA: for my $extra (@extras) {
                next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra);
@@ -489,76 +591,64 @@ EXTRA:    for my $extra (@extras) {
        for my $extra (sort {$a cmp $b} keys(%extras)) {
                my $text = $extras{$extra};
                if ($text =~ m/needed by/ && $text !~ m/enabled/) {
-                       printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? ")" : "");
+                       printf "\e[31;1;5m%-*s = %s%s\e[0m\n", $maxlen, module_shrink($extra), $text, ($text =~ m/needed by/ ? ")" : "");
                } else {
-                       printf "%-*s = %s%s\n", $maxlen, $extra, $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
+                       printf "%-*s = %s%s\n", $maxlen, module_shrink($extra), $text, ($text =~ m/needed by/ ? "\e[0m)" : "");
                }
        }
        return keys(%extras) if wantarray; # Can be used by manage_extras.
 }
 
-sub enable_extras (@) {
-       my (@extras) = @_;
-       for my $extra (@extras) {
-               my $extrapath = "src/modules/extra/$extra";
-               if (!-e $extrapath) {
-                       print STDERR "Cannot enable \e[32;1m$extra\e[0m : No such file or directory in src/modules/extra\n";
-                       next;
-               }
-               my $source = "src/modules/$extra";
-               if (-e $source) {
-                       print STDERR "Cannot enable \e[32;1m$extra\e[0m : destination in src/modules exists (might already be enabled?)\n";
-                       next;
+sub enable_extras(@) {
+       my $moduledir = catdir $RealDir, 'src', 'modules';
+       my $extradir = catdir $moduledir, 'extra';
+
+       for my $extra (@_) {
+               my $shortname = module_shrink $extra;
+               my $extrafile = module_expand $extra;
+
+               my $extrapath = catfile $extradir, $extrafile;
+               if (!-f $extrapath) {
+                       print_error "<|GREEN $extra|> is not an extra module!";
                }
-               # Get dependencies, and add them to be processed.
-               my @deps = split /\s+/, get_directive($extrapath, 'ModDep', '');
-               for my $dep (@deps) {
-                       next if scalar(grep { $_ eq $dep } (@extras)) > 0; # Skip if we're going to be enabling it anyway.
-                       if (!-e "src/modules/$dep" && !-e "include/$dep") {
-                               if (-e "src/modules/extra/$dep") {
-                                       print STDERR "Will also enable extra \e[32;1m$dep\e[0m (needed by \e[32;1m$extra\e[0m)\n";
-                                       push @extras, $dep;
-                               } else {
-                                       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";
-                               }
+
+               my $modulepath = catfile $moduledir, $extrafile;
+               if (-l $modulepath) {
+                       if (readlink($modulepath) ne $extrapath) {
+                               unlink $modulepath; # Remove the dead symlink.
+                       } else {
+                               next; # Module is already enabled.
                        }
                }
-               print "Enabling $extra ... \n";
-               symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n";
+
+               if (-e $modulepath) {
+                       print_error "unable to symlink <|GREEN ${\abs2rel $modulepath}|> to <|GREEN ${\abs2rel $extrapath}|>: the target exists and is not a symlink.";
+               } else {
+                       say console_format "Enabling the <|GREEN $shortname|> module ...";
+                       symlink $extrapath, $modulepath or print_error "unable to symlink <|GREEN ${\abs2rel $modulepath}|> to <|GREEN ${\abs2rel $extrapath}|>: $!";
+               }
        }
 }
 
-sub disable_extras (@)
-{
-       opendir my $dd, "src/modules/extra/";
-       my @files = readdir($dd);
-       closedir $dd;
-       my (@extras) = @_;
-EXTRA: for my $extra (@extras) {
-               my $extrapath = "src/modules/extra/$extra";
-               my $source = "src/modules/$extra";
-               if (!-e $extrapath) {
-                       print STDERR "Cannot disable \e[32;1m$extra\e[0m : Is not an extra\n";
-                       next;
-               }
-               if ((! -l $source) || readlink($source) ne "extra/$extra") {
-                       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";
-                       next;
-               }
-               # Check if anything needs this.
-               for my $file (@files) {
-                       my @deps = split /\s+/, get_directive("src/modules/extra/$file", 'ModDep', '');
-                       # File depends on this extra...
-                       if (scalar(grep { $_ eq $extra } @deps) > 0) {
-                               # And is both enabled and not about to be disabled.
-                               if (-e "src/modules/$file" && scalar(grep { $_ eq $file } @extras) < 1) {
-                                       print STDERR "Cannot disable \e[32;1m$extra\e[0m : is needed by \e[32;1m$file\e[0m\n";
-                                       next EXTRA;
-                               }
-                       }
+sub disable_extras(@) {
+       my $moduledir = catdir $RealDir, 'src', 'modules';
+       my $extradir = catdir $moduledir, 'extra';
+
+       for my $extra (@_) {
+               my $shortname = module_shrink $extra;
+               my $extrafile = module_expand $extra;
+
+               my $modulepath = catfile $moduledir, $extrafile;
+               my $extrapath = catfile $extradir, $extrafile;
+               if (!-e $modulepath && !-e $extrapath) {
+                       print_error "the <|GREEN $shortname|> module does not exist!";
+               } elsif (!-e $modulepath && -e $extrapath) {
+                       print_error "the <|GREEN $shortname|> module is not currently enabled!";
+               } elsif ((-e $modulepath && !-e $extrapath) || !-l $modulepath) {
+                       print_error "the <|GREEN $shortname|> module is not an extra module!";
+               } else {
+                       say console_format "Disabling the <|GREEN $shortname|> module ...";
+                       unlink $modulepath or print_error "unable to unlink <|GREEN $extrapath|>: $!";
                }
-               # Now remove.
-               print "Disabling $extra ... \n";
-               unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n";
        }
 }