#!/usr/bin/perl ################################################### # InspIRCd Configuration Script # # Copyright 2002-2009 The InspIRCd Development Team # http://wiki.inspircd.org/Credits # # Licensed under GPL, please see the COPYING file # for more information # # $Id$ # ################################################### BEGIN { require 5.8.0; } use strict; use warnings FATAL => qw(all); use Data::Dumper; BEGIN { $Data::Dumper::Sortkeys = 1; $Data::Dumper::Useqq = 1; }; use File::Copy (); use Socket; use Cwd; use Getopt::Long; # Utility functions for our buildsystem use make::utilities; use make::configure; use make::gnutlscert; use make::opensslcert; ############################################################################################### # # EDITABLE VARIABLES # ############################################################################################### # If you wish to ignore a dependency throughout the entire core, add it here. our @ignoredeps = ( "inspircd_win32wrapper.h", # windows has its own configure program ); # If you wish for all files in the entire core to have a given dependency, insert it here. # You should keep this to an absolute minimum to avoid rebuilds that are not neccessary. our @immutabledeps = ( "inspircd_config.h", # auto re-generated by configure "inspircd.h", ); ############################################################################################### # # NON-EDITABLE VARIABLES # ############################################################################################### # List of commands that make up 'make install' and 'make deinstall' our $install_list = ""; our $uninstall_list = ""; our ($opt_use_gnutls, $opt_rebuild, $opt_use_openssl, $opt_nointeractive, $opt_ports, $opt_epoll, $opt_kqueue, $opt_noports, $opt_noepoll, $opt_nokqueue, $opt_ipv6, $opt_ipv6links, $opt_noipv6links, $opt_maxbuf, $opt_disable_debug, $opt_freebsd_port); our ($opt_cc, $opt_base_dir, $opt_config_dir, $opt_module_dir, $opt_binary_dir, $opt_library_dir); sub list_extras (); sub enable_extras (@); sub disable_extras (@); my @opt_enableextras; my @opt_disableextras; GetOptions ( 'enable-gnutls' => \$opt_use_gnutls, 'rebuild' => \$opt_rebuild, 'enable-openssl' => \$opt_use_openssl, 'disable-interactive' => \$opt_nointeractive, 'enable-ports' => \$opt_ports, 'enable-epoll' => \$opt_epoll, 'enable-kqueue' => \$opt_kqueue, 'disable-ports' => \$opt_noports, 'disable-epoll' => \$opt_noepoll, 'disable-kqueue' => \$opt_nokqueue, 'enable-ipv6' => \$opt_ipv6, 'enable-remote-ipv6' => \$opt_ipv6links, 'disable-remote-ipv6' => \$opt_noipv6links, 'with-cc=s' => \$opt_cc, 'with-maxbuf=i' => \$opt_maxbuf, 'enable-freebsd-ports-openssl' => \$opt_freebsd_port, 'prefix=s' => \$opt_base_dir, 'config-dir=s' => \$opt_config_dir, 'module-dir=s' => \$opt_module_dir, 'binary-dir=s' => \$opt_binary_dir, 'library-dir=s' => \$opt_library_dir, 'disable-debuginfo' => sub { $opt_disable_debug = 1 }, 'help' => sub { showhelp(); }, 'modupdate' => sub { modupdate(); }, 'update' => sub { update(); }, 'svnupdate' => sub { svnupdate(); }, 'clean' => sub { clean(); }, 'list-extras' => sub { list_extras; exit 0; }, # This, --enable-extras, and --disable-extras are for non-interactive managing. 'enable-extras=s@' => \@opt_enableextras, # ^ 'disable-extras=s@' => \@opt_disableextras, # ^ 'generate-openssl-cert' => sub { make_openssl_cert(); exit(0); }, 'generate-gnutls-cert' => sub { make_gnutls_cert(); exit(0); } ); if (scalar(@opt_enableextras) + scalar(@opt_disableextras) > 0) { @opt_enableextras = split /,/, join(',', @opt_enableextras); @opt_disableextras = split /,/, join(',', @opt_disableextras); enable_extras(@opt_enableextras); disable_extras(@opt_disableextras); list_extras; print "Remember: YOU are responsible for making sure any libraries needed have been installed!\n"; print "Run $0 -modupdate after you've done this to update the makefiles.\n"; exit 0; } our $non_interactive = ( (defined $opt_library_dir) || (defined $opt_base_dir) || (defined $opt_config_dir) || (defined $opt_module_dir) || (defined $opt_base_dir) || (defined $opt_binary_dir) || (defined $opt_nointeractive) || (defined $opt_cc) || (defined $opt_ipv6) || (defined $opt_ipv6links) || (defined $opt_noipv6links) || (defined $opt_kqueue) || (defined $opt_epoll) || (defined $opt_ports) || (defined $opt_use_openssl) || (defined $opt_nokqueue) || (defined $opt_noepoll) || (defined $opt_noports) || (defined $opt_maxbuf) || (defined $opt_use_gnutls) || (defined $opt_freebsd_port) ); our $interactive = !$non_interactive; chomp(our $topdir = getcwd()); our $this = resolve_directory($topdir); # PWD, Regardless. our @modlist = (); # Declare for Module List.. our %config = (); # Initiate Configuration Hash.. $config{ME} = resolve_directory($topdir); # Present Working Directory $config{BASE_DIR} = $config{ME}; if (defined $opt_base_dir) { $config{BASE_DIR} = $opt_base_dir; } $config{CONFIG_DIR} = resolve_directory($config{BASE_DIR}."/conf"); # Configuration Directory $config{MODULE_DIR} = resolve_directory($config{BASE_DIR}."/modules"); # Modules Directory $config{BINARY_DIR} = resolve_directory($config{BASE_DIR}."/bin"); # Binary Directory $config{LIBRARY_DIR} = resolve_directory($config{BASE_DIR}."/lib"); # Library Directory if (defined $opt_config_dir) { $config{CONFIG_DIR} = $opt_config_dir; } if (defined $opt_module_dir) { $config{MODULE_DIR} = $opt_module_dir; } if (defined $opt_binary_dir) { $config{BINARY_DIR} = $opt_binary_dir; } if (defined $opt_library_dir) { $config{LIBRARY_DIR} = $opt_library_dir; } chomp($config{HAS_GNUTLS} = `pkg-config --modversion gnutls 2>/dev/null | cut -c 1,2,3`); # GNUTLS Version. if (defined $opt_freebsd_port) { chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`); chomp($config{HAS_OPENSSL_PORT} = `pkg-config --modversion openssl 2>/dev/null`); $config{USE_FREEBSD_BASE_SSL} = "n"; } else { if ($^O eq "freebsd") { # default: use base ssl chomp($config{HAS_OPENSSL} = `openssl version | cut -d ' ' -f 2`); # OpenSSL version, freebsd specific chomp($config{HAS_OPENSSL_PORT} = `pkg-config --modversion openssl 2>/dev/null`); # Port version, may be different } else { chomp($config{HAS_OPENSSL} = `pkg-config --modversion openssl 2>/dev/null`); # Openssl version, others $config{HAS_OPENSSL_PORT} = ""; } } chomp(our $gnutls_ver = $config{HAS_GNUTLS}); chomp(our $openssl_ver = $config{HAS_OPENSSL}); $config{USE_GNUTLS} = "n"; if (defined $opt_use_gnutls) { $config{USE_GNUTLS} = "y"; # Use gnutls. } $config{USE_OPENSSL} = "n"; # Use openssl. if (defined $opt_use_openssl) { $config{USE_OPENSSL} = "y"; } # no, let's not change these. $config{OPTIMITEMP} = "0"; # Default Optimisation Value if (!defined $opt_disable_debug) { $config{OPTIMISATI} = "-g1"; # Optimisation Flag } else { $config{OPTIMISATI} = "-O2"; # DEBUGGING OFF! } $config{HAS_STRLCPY} = "false"; # strlcpy Check. $config{HAS_STDINT} = "false"; # stdint.h check $config{USE_KQUEUE} = "y"; # kqueue enabled if (defined $opt_kqueue) { $config{USE_KQUEUE} = "y"; } if (defined $opt_nokqueue) { $config{USE_KQUEUE} = "n"; } $config{USE_POLL} = "y"; # poll enabled $config{USE_EPOLL} = "y"; # epoll enabled if (defined $opt_epoll) { $config{USE_EPOLL} = "y"; } if (defined $opt_noepoll) { $config{USE_EPOLL} = "n"; } $config{USE_PORTS} = "y"; # epoll enabled if (defined $opt_ports) { $config{USE_PORTS} = "y"; } if (defined $opt_noports) { $config{USE_PORTS} = "n"; } $config{IPV6} = "n"; # IPv6 support (experimental) if (defined $opt_ipv6) { $config{IPV6} = "y"; } $config{SUPPORT_IP6LINKS} = "y"; # IPv4 supporting IPv6 links (experimental) if (defined $opt_ipv6links) { $config{SUPPORT_IP6LINKS} = "y"; } if (defined $opt_noipv6links) { $config{SUPPORT_IP6LINKS} = "n"; } chomp($config{GCCVER} = `g++ -dumpversion | cut -c 1`); # Major GCC Version chomp($config{GCCMINOR} = `g++ -dumpversion | cut -c 3`); $config{_SOMAXCONN} = SOMAXCONN; # Max connections in accept queue $config{OSNAME} = $^O; # Operating System Name $config{IS_DARWIN} = "NO"; # Is OSX? $config{STARTSCRIPT} = "inspircd"; # start script? $config{DESTINATION} = "BASE"; # Is target path. $config{EXTRA_DIR} = ""; # Is empty. if ($config{OSNAME} =~ /darwin/i) { $config{IS_DARWIN} = "YES"; $config{STARTSCRIPT} = "org.inspircd.plist"; # start script for OSX. $config{DESTINATION} = "LAUNCHDPATH"; # Is OSX target. $config{EXTRA_DIR} = " launchd_dir"; # Is OSX specific path. } $config{CC} = "g++"; # C++ compiler if (defined $opt_cc) { $config{CC} = $opt_cc; } our $exec = $config{CC} . " -dumpversion | cut -c 1"; chomp($config{GCCVER} = `$exec`); # Major GCC Version $exec = $config{CC} . " -dumpversion | cut -c 3"; chomp($config{GCCMINOR} = `$exec`); $config{MAXBUF} = "512"; # Max buffer size if ($config{HAS_OPENSSL} =~ /^([-[:digit:].]+)([a-z])?(\-[a-z][0-9])?$/) { $config{HAS_OPENSSL} = $1; } else { $config{HAS_OPENSSL} = ""; } if (($config{GCCVER} eq "") || ($config{GCCMINOR} eq "")) { print $config{CC} . " was not found! You require g++ (the GNU C++ compiler, part of GCC) to build InspIRCd!\n"; exit; } # Get and Set some important vars.. getmodules(); sub clean { unlink(".config.cache"); } our ($has_epoll, $has_ports, $has_kqueue) = (0, 0, 0); sub update { eval { chomp($topdir = getcwd()); $this = resolve_directory($topdir); # PWD, Regardless. getmodules(); # Does the cache file exist? if (!getcache()) { # No, No it doesn't.. *BASH* print "You have not run ./configure before. Please do this before trying to run the update script.\n"; exit 0; } else { # We've Loaded the cache file and all our variables.. print "Updating files...\n"; getosflags(); if (defined($opt_disable_debug) && $opt_disable_debug == 1) { print "Disabling debug information (-g).\n"; $config{OPTIMISATI} = ""; getosflags(); } $has_epoll = $config{HAS_EPOLL}; $has_ports = $config{HAS_PORTS}; $has_kqueue = $config{HAS_KQUEUE}; writefiles(1); makecache(); print "Complete.\n"; exit; } }; if ($@) { print "Configure update failed: $@\n"; } exit; } sub modupdate { eval { chomp($topdir = getcwd()); $this = resolve_directory($topdir); # PWD, Regardless. getmodules(); # Does the cache file exist? if (!getcache()) { # No, No it doesn't.. *BASH* print "You have not run ./configure before. Please do this before trying to run the update script.\n"; exit 0; } else { # We've Loaded the cache file and all our variables.. print "Updating files...\n"; getosflags(); $has_epoll = $config{HAS_EPOLL}; $has_ports = $config{HAS_PORTS}; $has_kqueue = $config{HAS_KQUEUE}; writefiles(0); makecache(); print "Complete.\n"; exit; } }; if ($@) { print "Module update failed: $@\n"; } exit; } sub svnupdate { my $fail = 0; open(FH,"<.svn/entries") or $fail = 1; if ($fail) { print "This is not an SVN copy of InspIRCd.\n"; exit 1; } else { close(FH); } open my $fd, "-|", "svn update"; my $configurechanged = 0; # Needs ./configure -update my $coredirchanged = 0; # Needs ./configure -update my $moduledirchanged = 0; # Needs ./configure -modupdate my $rootincchanged = 0; my @conflicted = (); while (defined(my $line = <$fd>)) { my ($action, $file); print $line; $line =~ m/^([ADUCG])\s+(.*)$/ or next; ($action, $file) = ($1, $2); if ($action eq "C") { push @conflicted, $file; if ($file eq "configure") { $configurechanged = 1; } elsif ($file =~ m#^src/modules#) { $moduledirchanged = 1; } elsif ($file =~ m#^src/#) { $coredirchanged = 1; } elsif ($file =~ m/^\..*\.inc$/) { $rootincchanged = 1; } } elsif ($action eq "U" || $action eq "G") { if ($file eq "configure") { $configurechanged = 1; } elsif ($file =~ m/^\..*\.inc$/) { $rootincchanged = 1; } } elsif ($action eq "A" || $action eq "D") { if ($file =~ m#^src/modules#) { $moduledirchanged = 1; } elsif ($file =~ m#^src/#) { $coredirchanged = 1; } } } unless (close $fd) # close() waits for exit and returns false if the command failed { if ($! == 0) { print STDERR "Problem updating from SVN, please check above for errors\n"; } else { print STDERR "Failed to run SVN: $!\n"; } exit 1; } if (scalar(@conflicted) > 0) { print STDERR "\e[0;33;1mERROR:\e[0m You have local modifications which conflicted with the updates from SVN\n"; printf STDERR "Configure is not able to complete the update. Please resolve these conflicts, then run ./configure -%supdate\n", (($coredirchanged || $configurechanged) ? "" : "mod"); print "Conflicted files: " . join ", ", @conflicted . "\n"; exit 1; } if ($configurechanged || $coredirchanged) { system("perl configure -update"); } elsif ($moduledirchanged || $rootincchanged) { system("perl configure -modupdate"); } else { print "No need to update Makefiles.\n"; } if (defined $opt_rebuild) { system("make install"); } exit; } sub test_compile { my $feature = shift; my $fail = 0; $fail ||= system "$config{CC} -o test_$feature make/check_$feature.cpp >/dev/null 2>&1"; $fail ||= system "./test_$feature"; unlink "test_$feature"; return !$fail; } print "Running non-interactive configure...\n" unless $interactive; print "Checking for cache from previous configure... "; print ((!getcache()) ? "not found\n" : "found\n"); print "Checking operating system version... "; print getosflags() . "\n"; printf "Checking if stdint.h exists... "; $config{HAS_STDINT} = "true"; our $fail = 0; open(STDINT, ")) { chomp($line); # try and find the delcaration of: # size_t strlcpy(...) if ($line =~ /size_t(\0x9|\s)+strlcpy/) { $config{HAS_STRLCPY} = "true"; } } close(STRLCPY); } print "yes\n" if $config{HAS_STRLCPY} eq "true"; print "no\n" if $config{HAS_STRLCPY} eq "false"; printf "Checking if kqueue exists... "; $has_kqueue = 0; $fail = 0; open(KQUEUE, ")) { chomp($line); # try and find the delcaration of: # int kqueue(void); if ($line =~ /int(\0x9|\s)+kqueue/) { $has_kqueue = 1; } } close(KQUEUE); } print "yes\n" if $has_kqueue == 1; print "no\n" if $has_kqueue == 0; printf "Checking for epoll support... "; $has_epoll = test_compile('epoll'); print $has_epoll ? "yes\n" : "no\n"; printf "Checking for eventfd support... "; $config{HAS_EVENTFD} = test_compile('eventfd') ? 'true' : 'false'; print $config{HAS_EVENTFD} eq 'true' ? "yes\n" : "no\n"; printf "Checking if Solaris I/O completion ports are available... "; $has_ports = 0; our $system = `uname -s`; chomp ($system); $has_ports = 1 if ($system eq "SunOS"); if ($has_ports) { my $kernel = `uname -r`; chomp($kernel); if (($kernel !~ /^5\.1./)) { $has_ports = 0; } } print "yes\n" if $has_ports == 1; print "no\n" if $has_ports == 0; $config{HAS_EPOLL} = $has_epoll; $config{HAS_KQUEUE} = $has_kqueue; printf "Checking for libgnutls... "; if (defined($config{HAS_GNUTLS}) && (($config{HAS_GNUTLS}) || ($config{HAS_GNUTLS} eq "y"))) { if (defined($gnutls_ver) && ($gnutls_ver ne "")) { print "yes\n"; $config{HAS_GNUTLS} = "y"; } else { print "no\n"; $config{HAS_GNUTLS} = "n"; } } else { print "no\n"; $config{HAS_GNUTLS} = "n"; } printf "Checking for openssl... "; if (defined($config{HAS_OPENSSL}) && (($config{HAS_OPENSSL}) || ($config{HAS_OPENSSL} eq "y"))) { if (defined($openssl_ver) && ($openssl_ver ne "")) { print "yes\n"; $config{HAS_OPENSSL} = "y"; } else { print "no\n"; $config{HAS_OPENSSL} = "n"; } } else { print "no\n"; $config{HAS_OPENSSL} = "n"; } printf "Checking if you are running an ancient, unsupported OS... "; if ($config{OSNAME} =~ /FreeBSD/i) { my $version = `uname -r`; if ($version =~ /^4\./) { my $foundit = `ls -l /usr/local/lib/libgnugetopt* | wc -l`; if ($foundit > 0) { # ICKY ICKY ICK, FREEBSD 4.x! GET AN UPGRADE! $config{CRAQ} = "-L/usr/local/lib -lgnugetopt -DHAVE_DECL_GETOPT=1"; print "yes (upgrade ffs, freebsd 4 is *way* out of date)\n"; } else { print "\n\nERROR: You require libgnugetopt (from ports or packages) to build InspIRCd on FreeBSD 4.11.\n"; } } else { $config{CRAQ} = " "; print "no ($version)\n"; } } else { $config{CRAQ} = " "; print "no ($config{OSNAME})\n"; } print "Checking for upgrades to extra and third party modules... "; system "./modulemanager upgrade"; ################################################################################ # BEGIN INTERACTIVE PART # ################################################################################ # Clear the Screen.. if ($interactive) { print "\e[2J\e[0G\e[0d"; # J = Erase in Display, 2 = Entire Screen, (G, d) = Move cursor to (..,..) my $wholeos = $^O; my $rev = getrevision(); # Display Introduction Message.. print <<"STOP" ; Welcome to the \e[1mInspIRCd\e[0m Configuration program! (\e[1minteractive mode\e[0m) \e[1mPackage maintainers: Type ./configure --help for non-interactive help\e[0m *** If you are unsure of any of these values, leave it blank for *** *** standard settings that will work, and your server will run *** *** using them. Please consult your IRC network admin if in doubt. *** Press \e[1m\e[0m to accept the default for any option, or enter a new value. Please note: You will \e[1mHAVE\e[0m to read the docs dir, otherwise you won't have a config file! Your operating system is: \e[1;32m$config{OSNAME}\e[0m ($wholeos) Your InspIRCd revision ID is \e[1;32mr$rev\e[0m STOP if ($rev eq "r0") { print " (Non-SVN build)"; } print ".\n\n"; $config{CHANGE_COMPILER} = "n"; print "I have detected the following compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n"; while (($config{GCCVER} < 3) || ($config{GCCVER} eq "")) { print "\e[1;32mIMPORTANT!\e[0m A GCC 2.x compiler has been detected, and should NOT be used. You should probably specify a newer compiler.\n\n"; yesno('CHANGE_COMPILER',"Do you want to change the compiler?"); if ($config{CHANGE_COMPILER} =~ /y/i) { print "What command do you want to use to invoke your compiler?\n"; print "[\e[1;32m$config{CC}\e[0m] -> "; chomp($config{CC} = ); if ($config{CC} eq "") { $config{CC} = "g++"; } chomp(my $foo = `$config{CC} -dumpversion | cut -c 1`); if ($foo ne "") { chomp($config{GCCVER} = `$config{CC} -dumpversion | cut -c 1`); # we must redo these if we change compilers chomp($config{GCCMINOR} = `$config{CC} -dumpversion | cut -c 3`); print "Queried compiler: \e[1;32m$config{CC}\e[0m (version \e[1;32m$config{GCCVER}.$config{GCCMINOR}\e[0m)\n"; if ($config{GCCVER} < 3) { print "\e[1;32mGCC 2.x WILL NOT WORK!\e[0m. Let's try that again, shall we?\n"; } } else { print "\e[1;32mWARNING!\e[0m Could not execute the compiler you specified. You may want to try again.\n"; } } } print "\n"; # Directory Settings.. my $tmpbase = $config{BASE_DIR}; dir_check("do you wish to install the InspIRCd base", "BASE_DIR"); if ($tmpbase ne $config{BASE_DIR}) { $config{CONFIG_DIR} = resolve_directory($config{BASE_DIR}."/conf"); # Configuration Dir $config{MODULE_DIR} = resolve_directory($config{BASE_DIR}."/modules"); # Modules Directory $config{BINARY_DIR} = resolve_directory($config{BASE_DIR}."/bin"); # Binary Directory $config{LIBRARY_DIR} = resolve_directory($config{BASE_DIR}."/lib"); # Library Directory } dir_check("are the configuration files", "CONFIG_DIR"); dir_check("are the modules to be compiled to", "MODULE_DIR"); dir_check("is the IRCd binary to be placed", "BINARY_DIR"); dir_check("are the IRCd libraries to be placed", "LIBRARY_DIR"); my $chose_hiperf = 0; if ($has_kqueue) { yesno('USE_KQUEUE',"You are running a BSD operating system, and kqueue\nwas detected. Would you like to enable kqueue support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable kqueue?"); print "\n"; if ($config{USE_KQUEUE} eq "y") { $chose_hiperf = 1; } } if ($has_epoll) { yesno('USE_EPOLL',"You are running a Linux 2.6+ operating system, and epoll\nwas detected. Would you like to enable epoll support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable epoll?"); print "\n"; if ($config{USE_EPOLL} eq "y") { $chose_hiperf = 1; } } if ($has_ports) { yesno('USE_PORTS',"You are running Solaris 10.\nWould you like to enable I/O completion ports support?\nThis is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable support for I/O completion ports?"); print "\n"; if ($config{USE_PORTS} eq "y") { $chose_hiperf = 1; } } if (!$chose_hiperf) { yesno('USE_POLL', "Would you like to use poll?\n This is likely to increase performance.\nIf you are unsure, answer yes.\n\nEnable poll?\n"); if ($config{USE_POLL} ne "y") { print "No high-performance socket engines are available, or you chose\n"; print "not to enable one. Defaulting to select() engine.\n\n"; } } yesno('IPV6',"Would you like to build InspIRCd with IPv6 support?"); print "\n"; if ($config{IPV6} eq "y") { print "You have chosen to build an \e[1;32mIPV6-enabled\e[0m server.\nTo accept IPV4 users, you can still use IPV4 addresses\nin your port bindings..\n\n"; $config{SUPPORT_IP6LINKS} = "y"; } else { yesno('SUPPORT_IP6LINKS',"You have chosen to build an \e[1;32mIPV4-only\e[0m server.\nWould you like to enable support for linking to IPV6-enabled\nInspIRCd servers? If you are using a recent operating system and are\nunsure, answer yes. If you answer 'no' here, your InspIRCd server will\nbe unable to parse IPV6 addresses (e.g. for CIDR bans)\n\nEnable linking to servers which have IPV6 enabled?"); print "\n"; } $config{USE_FREEBSD_BASE_SSL} = "n"; $config{USE_FREEBSD_PORTS_SSL} = "n"; if ($config{HAS_OPENSSL_PORT} ne "") { $config{USE_FREEBSD_PORTS_SSL} = "y"; print "I have detected the OpenSSL FreeBSD port installed on your system,\n"; print "version \e[1;32m".$config{HAS_OPENSSL_PORT}."\e[0m. Your base system OpenSSL is version \e[1;32m".$openssl_ver."\e[0m.\n\n"; yesno('USE_FREEBSD_PORTS_SSL', "Do you want to use the FreeBSD ports version?"); print "\n"; $config{USE_FREEBSD_BASE_SSL} = "y" if ($config{USE_FREEBSD_PORTS_SSL} eq "n"); if ($config{USE_FREEBSD_BASE_SSL} eq "n") { # update to port version $openssl_ver = $config{HAS_OPENSSL_PORT}; } } else { $config{USE_FREEBSD_BASE_SSL} = "y" if ($^O eq "freebsd"); } $config{USE_SSL} = "n"; if ($config{HAS_GNUTLS} eq "y" || $config{HAS_OPENSSL} eq "y") { print "Detected GnuTLS version: \e[1;32m" . $gnutls_ver . "\e[0m\n"; print "Detected OpenSSL version: \e[1;32m" . $openssl_ver . "\e[0m\n\n"; yesno('USE_SSL', "One or more SSL libraries detected. Would you like to enable SSL support?"); if ($config{USE_SSL} eq "y") { if ($config{HAS_GNUTLS} eq "y") { yesno('USE_GNUTLS',"Would you like to enable SSL with m_ssl_gnutls? (recommended)"); if ($config{USE_GNUTLS} eq "y") { print "\nUsing GnuTLS SSL module.\n"; } } if ($config{HAS_OPENSSL} eq "y") { yesno('USE_OPENSSL', "Would you like to enable SSL with m_ssl_openssl?"); if ($config{USE_OPENSSL} eq "y") { print "\nUsing OpenSSL SSL module.\nYou will get better performance if you move to GnuTLS in the future.\n"; } } } } else { print "\nCould not detect OpenSSL or GnuTLS. Make sure pkg-config is installed if\n"; print "you intend to use OpenSSL, or that GnuTLS is in your path if you intend\nto use GnuTLS.\n\n"; } } dumphash(); if (($config{USE_GNUTLS} eq "y") && ($config{HAS_GNUTLS} ne "y")) { print "Sorry, but i couldn't detect gnutls. Make sure gnutls-config is in your path.\n"; exit(0); } if (($config{USE_OPENSSL} eq "y") && ($config{HAS_OPENSSL} ne "y")) { print "Sorry, but i couldn't detect openssl. Make sure openssl is in your path.\n"; exit(0); } our $failed = 0; $config{CERTGEN} ||= 'y'; yesno('CERTGEN',"Would you like generate SSL certificates now?") if ($interactive && ($config{USE_GNUTLS} eq "y" || $config{USE_OPENSSL} eq "y")); if ($config{USE_GNUTLS} eq "y") { unless (-r "src/modules/m_ssl_gnutls.cpp") { print "Symlinking src/modules/m_ssl_gnutls.cpp from extra/\n"; symlink "extra/m_ssl_gnutls.cpp", "src/modules/m_ssl_gnutls.cpp" or print STDERR "Symlink failed: $!"; } if ($interactive && $config{CERTGEN} eq 'y') { unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") { print "SSL Certificates Not found, Generating.. \n\n ************************************************************* * Generating the Private Key may take some time, go grab a * * Coffee. Even better, to generate some more entropy if it * * is taking a while, open another console and type du / a * * few times and get that HD going :) Then answer the * * Questions which follow. If you are unsure, just hit enter * *************************************************************\n\n"; $failed = make_gnutls_cert(); if ($failed) { print "\n\e[1;32mCertificate generation failed!\e[0m\n\n"; } else { print "\nCertificate generation complete, copying to config directory... "; File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n"; File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n"; print "Done.\n\n"; } } else { print "SSL Certificates found, skipping.\n\n"; } } else { print "Skipping SSL certificate generation\nin non-interactive mode.\n\n"; } } if ($config{USE_OPENSSL} eq "y") { unless (-r "src/modules/m_ssl_openssl.cpp") { print "Symlinking src/modules/m_ssl_openssl.cpp from extra/\n"; symlink "extra/m_ssl_openssl.cpp", "src/modules/m_ssl_openssl.cpp" or print STDERR "Symlink failed: $!"; } $failed = 0; if ($interactive && $config{CERTGEN} eq 'y') { unless (-r "$config{CONFIG_DIR}/key.pem" && -r "$config{CONFIG_DIR}/cert.pem") { print "SSL Certificates Not found, Generating.. \n\n ************************************************************* * Generating the certificates may take some time, go grab a * * coffee, or something. * *************************************************************\n\n"; make_openssl_cert(); print "\nCertificate generation complete, copying to config directory... "; File::Copy::move("key.pem", "$config{CONFIG_DIR}/key.pem") or print STDERR "Could not copy key.pem!\n"; File::Copy::move("cert.pem", "$config{CONFIG_DIR}/cert.pem") or print STDERR "Could not copy cert.pem!\n"; File::Copy::move("dhparams.pem", "$config{CONFIG_DIR}/dhparams.pem") or print STDERR "Could not copy dhparams.pem!\n"; print "Done.\n\n"; } else { print "SSL Certificates found, skipping.\n\n" } } else { print "Skipping SSL certificate generation\nin non-interactive mode.\n\n"; } } if (($config{USE_GNUTLS} eq "n") && ($config{USE_OPENSSL} eq "n")) { print "Skipping SSL Certificate generation, SSL support is not available.\n\n"; } getosflags(); writefiles(1); makecache(); print "\n\n"; print "To build your server with these settings, please type '\e[1;32m$config{MAKEPROG}\e[0m' now.\n"; if (($config{USE_GNUTLS} eq "y") || ($config{USE_OPENSSL} eq "y")) { print "Please note: for \e[1;32mSSL support\e[0m you will need to load required\n"; print "modules in your config. This configure script has added those modules to the\n"; print "build process. For more info please refer to:\n"; print "\e[1;32mhttp://wiki.inspircd.org/Installation_From_Tarball\e[0m\n"; } print "*** \e[1;32mRemember to edit your configuration files!!!\e[0m ***\n\n\n"; if (($config{OSNAME} eq "OpenBSD") && ($config{CC} ne "eg++")) { print "\e[1;32mWARNING!\e[0m You are running OpenBSD but you are using the base gcc package\nrather than eg++. This compile will most likely fail, but i'm letting you\ngo ahead with it anyway, just in case i'm wrong :-)\n"; } if ($config{GCCVER} < "3") { print <) { chomp; # Ignore Blank lines, and comments.. next if /^\s*$/; next if /^\s*#/; my ($key, $value) = split("=", $_, 2); $value =~ /^\"(.*)\"$/; # Do something with data here! $config{$key} = $1; } close(CACHE); return 1; } sub makecache { # Dump the contents of %config print "Writing \e[1;32mcache file\e[0m for future ./configures ...\n"; open(FILEHANDLE, ">.config.cache"); foreach my $key (keys %config) { print FILEHANDLE "$key=\"$config{$key}\"\n"; } close(FILEHANDLE); } sub dir_check { my ($desc, $hash_key) = @_; my $complete = 0; while (!$complete) { print "In what directory $desc?\n"; print "[\e[1;32m$config{$hash_key}\e[0m] -> "; chomp(my $var = ); if ($var eq "") { $var = $config{$hash_key}; } if ($var =~ /^\~\/(.+)$/) { # Convert it to a full path.. $var = resolve_directory($ENV{HOME} . "/" . $1); } elsif ((($config{OSNAME} =~ /MINGW32/i) and ($var !~ /^[A-Z]{1}:\\.*/)) and (substr($var,0,1) ne "/")) { # Assume relative Path was given.. fill in the rest. $var = $this . "/$var"; } $var = resolve_directory($var); if (! -e $var) { print "$var does not exist. Create it?\n[\e[1;32my\e[0m] "; chomp(my $tmp = ); if (($tmp eq "") || ($tmp =~ /^y/i)) { # Attempt to Create the Dir.. my $chk = eval { use File::Path (); File::Path::mkpath($var, 0, 0777); 1; }; unless (defined($chk) && -d $var) { print "Unable to create directory. ($var)\n\n"; # Restart Loop.. next; } } else { # They said they don't want to create, and we can't install there. print "\n\n"; next; } } else { if (!is_dir($var)) { # Target exists, but is not a directory. print "File $var exists, but is not a directory.\n\n"; next; } } # Either Dir Exists, or was created fine. $config{$hash_key} = $var; $complete = 1; print "\n"; } } our $SHARED = ""; sub getosflags { # Beware: Linux sets it's own cflags below for some retarded reason $config{LDLIBS} = "-pthread -lstdc++"; $config{FLAGS} = "-pipe -fPIC -Woverloaded-virtual -Wshadow -Wformat=2 -Wmissing-format-attribute -Wall $config{OPTIMISATI}"; $config{DEVELOPER} = "-pipe -fPIC -Woverloaded-virtual -Wshadow -Wall -Wformat=2 -Wmissing-format-attribute -g"; $SHARED = "-shared -export-dynamic"; $config{MAKEPROG} = "make"; if ($config{OSNAME} =~ /darwin/i) { $config{FLAGS} = "-pipe -DDARWIN -frtti -fPIC -Wall $config{OPTIMISATI}"; $SHARED = "-bundle -twolevel_namespace -undefined dynamic_lookup"; $config{LDLIBS} = "-ldl -pthread -lstdc++"; } if ($config{OSNAME} =~ /OpenBSD/i) { $config{MAKEPROG} = "gmake"; # apparantly (Dagonet says) that this causes problems, so let's try without it. # $config{LDLIBS} = $config{LDLIBS} . " -lunwind"; chomp(my $foo = `eg++ -dumpversion | cut -c 1`); # theyre running the package version of gcc (eg++)... detect it and set up its version numbers. # if theyre not running this, configure lets the build continue but they probably wont manage to # compile as this standard version is 2.95.3! if ($foo ne "") { $config{CC} = "eg++"; chomp($config{GCCVER} = `eg++ -dumpversion | cut -c 1`); # we must redo these if we change the compiler path chomp($config{GCCMINOR} = `eg++ -dumpversion | cut -c 3`); } return "OpenBSD"; } if ($config{OSNAME} =~ /Linux/i) { $config{LDLIBS} = "-ldl -lstdc++ -pthread"; # $config{FLAGS} = "-fPIC -Woverloaded-virtual -Wshadow -Wall $config{OPTIMISATI}"; $config{FLAGS} .= " " . $ENV{CXXFLAGS} if exists($ENV{CXXFLAGS}); $config{LDLIBS} .= " " . $ENV{LDLIBS} if exists($ENV{LDLIBS}); $config{LDLIBS} .= " " . $ENV{LDFLAGS} if exists($ENV{LDFLAGS}); $config{MAKEPROG} = "make"; } if ($config{OSNAME} =~ /FreeBSD/i) { $config{FLAGS} .= " " . $ENV{CXXFLAGS} if exists($ENV{CXXFLAGS}); $config{LDLIBS} .= " " . $ENV{LDLIBS} if exists($ENV{LDLIBS}); $config{LDLIBS} .= " " . $ENV{LDFLAGS} if exists($ENV{LDFLAGS}); } if ($config{OSNAME} =~ /SunOS/i or $config{OSNAME} =~ /solaris/i) { # solaris/sunos needs these # socket = bsd sockets api # nsl = dns stuff # rt = POSIX realtime extensions # resolv = inet_aton only (why isnt this in nsl?!) $config{MAKEPROG} = "gmake"; $config{LDLIBS} .= " -lsocket -lnsl -lrt -lresolv -pthread"; return "Solaris"; } if($config{OSNAME} =~ /MINGW32/i) { # All code is position-independent on windows $config{FLAGS} =~ s/-fPIC //; return "MinGW"; } return $config{OSNAME}; } my ($mliflags, $mfrules, $mobjs, $mfcount) = ("", "", "", 0); sub writefiles { my($writeheader) = @_; # First File.. inspircd_config.h chomp(my $incos = `uname -n -s -r`); chomp(my $version = `sh src/version.sh`); chomp(my $revision2 = getrevision()); if ($writeheader == 1) { print "Writing \e[1;32minspircd_config.h\e[0m\n"; open(FILEHANDLE, ">include/inspircd_config.h"); print FILEHANDLE <= 3) { print FILEHANDLE "#define GCC3\n"; } if ( (($config{GCCVER} == 4) && ($config{GCCMINOR} >= 3)) || ($config{GCCVER} > 4) ) { print FILEHANDLE "#define HASHMAP_DEPRECATED\n"; } if ($config{HAS_STRLCPY} eq "true") { print FILEHANDLE "#define HAS_STRLCPY\n"; } if ($config{HAS_STDINT} eq "true") { print FILEHANDLE "#define HAS_STDINT\n"; } if ($config{IPV6} =~ /y/i) { print FILEHANDLE "#define IPV6\n"; } if ($config{SUPPORT_IP6LINKS} =~ /y/i) { print FILEHANDLE "#define SUPPORT_IP6LINKS\n"; } if ($config{HAS_EVENTFD} eq 'true') { print FILEHANDLE "#define HAS_EVENTFD\n"; } my $use_hiperf = 0; if (($has_kqueue) && ($config{USE_KQUEUE} eq "y")) { print FILEHANDLE "#define USE_KQUEUE\n"; $config{SOCKETENGINE} = "socketengine_kqueue"; $use_hiperf = 1; } if (($has_epoll) && ($config{USE_EPOLL} eq "y")) { print FILEHANDLE "#define USE_EPOLL\n"; $config{SOCKETENGINE} = "socketengine_epoll"; $use_hiperf = 1; } if (($has_ports) && ($config{USE_PORTS} eq "y")) { print FILEHANDLE "#define USE_PORTS\n"; $config{SOCKETENGINE} = "socketengine_ports"; $use_hiperf = 1; } # user didn't choose either epoll or select for their OS. # default them to USE_SELECT (ewwy puke puke) if (!$use_hiperf) { print "no hi-perf, " . $config{USE_POLL}; if ($config{USE_POLL} eq "y") { print FILEHANDLE "#define USE_POLL\n"; $config{SOCKETENGINE} = "socketengine_poll"; } else { print FILEHANDLE "#define USE_SELECT\n"; $config{SOCKETENGINE} = "socketengine_select"; } } print FILEHANDLE "\n#include \"threadengines/threadengine_pthread.h\"\n\n#endif\n"; close(FILEHANDLE); } if ($writeheader) { open(FILEHANDLE, ">include/inspircd_se_config.h"); print FILEHANDLE <; close(FILEHANDLE); print "Writing \e[1;32m$file\e[0m ...\n"; for my $var (qw( CC FLAGS DEVELOPER LDLIBS BASE_DIR CONFIG_DIR MODULE_DIR BINARY_DIR LIBRARY_DIR STARTSCRIPT DESTINATION EXTRA_DIR SOCKETENGINE CORE_FLAGS )) { s/\@$var\@/$config{$var}/g; } s/\@EXECUTABLE\@/$exe/ if defined $exe; s/\@VERSION\@/$version/ if defined $version; s/\@INSTALL_LIST\@/$install_list/ if defined $install_list; s/\@UNINSTALL_LIST\@/$uninstall_list/ if defined $uninstall_list; if ($file eq 'Makefile') { my $mk_tmp = $_; s/\@IFDEF (\S+)/ifdef $1/g; s/\@IFNDEF (\S+)/ifndef $1/g; s/\@ELSE/else/g; s/\@ENDIF/endif/g; s/ *\@BSD_ONLY .*\n//g; s/\@GNU_ONLY //g; s/\@DO_EXPORT (.*)/export $1/g; open MKF, '>GNUmakefile' or die "Can't write to GNUmakefile: $!"; print MKF $_; close MKF; $_ = $mk_tmp; s/\@IFDEF (\S+)/.if defined($1)/g; s/\@IFNDEF (\S+)/.if !defined($1)/g; s/\@ELSE/.else/g; s/\@ENDIF/.endif/g; s/\@BSD_ONLY //g; s/ *\@GNU_ONLY .*\n//g; $mk_tmp = $_; $mk_tmp =~ s#\@DO_EXPORT (.*)#"MAKEENV += ".join ' ', map "$_='\${$_}'", split /\s/, $1#eg; open MKF, '>BSDmakefile' or die "Can't write to BSDmakefile: $!"; print MKF $mk_tmp; close MKF; } else { open(FILEHANDLE, ">$file") or die("Can't write to $file: $!\n"); print FILEHANDLE $_; close(FILEHANDLE); } } } closedir(DIRHANDLE); # Make inspircd executable! chmod 0744, 'inspircd'; } sub read_module_directory { my ($dpath, $reldpath) = @_; if (opendir(MDIRHANDLE, $dpath) == 0) { return; } foreach my $fname (sort readdir(MDIRHANDLE)) { if ($fname =~ /\.cpp$/) { my $cmflags = getcompilerflags("$dpath/$fname"); $mliflags = $mliflags . " " . getlinkerflags("$dpath/$fname"); my $deps = getdependencies("$dpath/$fname"); my $oname = $fname; $oname =~ s/\.cpp$/.o/g; $mfrules = $mfrules . "$reldpath/$oname: $reldpath/$fname ../../include/modules.h ../../include/users.h ../../include/channels.h ../../include/base.h ../../include/inspircd_config.h ../../include/inspircd.h ../../include/configreader.h $deps\n"; $mfrules = $mfrules . " \$(RUNCC) -I. \$(FLAGS) $cmflags $SHARED -o $reldpath/$oname -c $reldpath/$fname\n\n"; $mobjs = $mobjs . " $reldpath/$oname"; $mfcount++; } elsif ((-d "$dpath/$fname") && !($fname eq ".") && !($fname eq "..")) { read_module_directory($dpath."/".$fname, $reldpath."/".$fname); } } } sub calcdeps($) { # Yes i know we could use gcc -M but it seems to ideneify a lot of 'deep' # dependencies which are not relevent in C++. my $file = $_[0]; open (CPP, "<$file") or die("Can't open $file for reading!"); my %dupe = (); my $retlist = ""; foreach my $d (@ignoredeps) { $dupe{$d} = 1; } my $immutable = ""; foreach my $dep (@immutabledeps) { $immutable = $immutable . "../include/$dep "; } $immutable =~ s/ $//g; while (defined(my $line = )) { chomp($line); if ($line =~ /#include "(.+\.h)"/) { if (!exists($dupe{$1})) { $retlist = $retlist . "../include/$1 "; $dupe{$1} = 1; } } } close CPP; return length($immutable) ? $immutable . " " . $retlist : $retlist; } sub prepare_dynamic_makefile { my $i = 0; my @cmdlist = (); my %existing_install_list = (); my %core_files_list = (); opendir(DIRHANDLE, "src/commands"); foreach my $name (sort readdir(DIRHANDLE)) { if ($name =~ /^cmd_(.+)\.cpp$/) { $cmdlist[$i++] = $1; $install_list = $install_list . " -install -m \$(INSTMODE) src/commands/cmd_" . $1 . ".so \$(LIBPATH)\n"; $uninstall_list = $uninstall_list . " -rm \$(LIBPATH)/cmd_$1.so\n"; } } closedir(DIRHANDLE); if (!$has_epoll) { $config{USE_EPOLL} = 0; } if (!$has_kqueue) { $config{USE_KQUEUE} = 0; } if (!$has_ports) { $config{USE_PORTS} = 0; } foreach my $dir ("src/modules") { print "Scanning \e[1;32m$dir\e[0m for installation files "; opendir(DIRHANDLE, $dir); foreach my $name (sort readdir(DIRHANDLE)) { if ($name =~ /\.cpp$/) { open (CPP, "<$dir/$name") or die("Can't open $dir/$name to scan it! oh bugger"); print "."; while (defined(my $line = )) { chomp($line); if ($line =~ /\/\* \$CopyInstall: (.*?) \*\//i) { if (!exists($existing_install_list{$1})) { $existing_install_list{$1} = 1; my $idir = (split(' ',$1))[1]; my $ifile = (split(' ',$1))[0]; $install_list = $install_list . " -cp $1\n"; $ifile =~ s/.*\///g; $uninstall_list = $uninstall_list . " -rm $idir/$ifile\n"; } } } close CPP; } } closedir(DIRHANDLE); print " done!\n"; } if ($config{IS_DARWIN} eq "YES") { $config{CORE_FLAGS} = '-dynamic -bind_at_load -L.'; } else { $config{CORE_FLAGS} = (defined $config{CRAQ} ? $config{CRAQ}. ' ' : "").'-rdynamic -L.' } } # Routine to list out the extra/ modules that have been enabled. # Note: when getting any filenames out and comparing, it's important to lc it if the # file system is not case-sensitive (== Epoc, MacOS, OS/2 (incl DOS/DJGPP), VMS, Win32 # (incl NetWare, Symbian)). Cygwin may or may not be case-sensitive, depending on # configuration, however, File::Spec does not currently tell us (it assumes Unix behavior). sub list_extras () { use File::Spec; # @_ not used my $srcdir = File::Spec->catdir("src", "modules"); my $abs_srcdir = File::Spec->rel2abs($srcdir); local $_; my $dd; opendir $dd, File::Spec->catdir($abs_srcdir, "extra") or die (File::Spec->catdir($abs_srcdir, "extra") . ": $!\n"); my @extras = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd)); closedir $dd; undef $dd; opendir $dd, $abs_srcdir or die "$abs_srcdir: $!\n"; my @sources = map { File::Spec->case_tolerant() ? lc($_) : $_ } (readdir($dd)); closedir $dd; undef $dd; my $maxlen = (sort { $b <=> $a } (map {length($_)} (@extras)))[0]; my %extras = (); EXTRA: for my $extra (@extras) { next if (File::Spec->curdir() eq $extra || File::Spec->updir() eq $extra); next if ($extra eq '.svn'); my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra); my $abs_source = File::Spec->catfile($abs_srcdir, $extra); next unless ($extra =~ m/\.(cpp|h)$/ || (-d $abs_extra)); # C++ Source/Header, or directory if (-l $abs_source) { # Symlink, is it in the right place? my $targ = readlink($abs_source); my $abs_targ = File::Spec->rel2abs($targ, $abs_srcdir); if ($abs_targ eq $abs_extra) { $extras{$extra} = "\e[32;1menabled\e[0m"; } else { $extras{$extra} = sprintf("\e[31;1mwrong symlink target (%s)\e[0m", $abs_targ); } } elsif (-e $abs_source) { my ($devext, $inoext) = stat($abs_extra); my ($devsrc, $inosrc, undef, $lnksrc) = stat($abs_source); if ($lnksrc > 1) { if ($devsrc == $devext && $inosrc == $inoext) { $extras{$extra} = "\e[32;1menabled\e[0m"; } else { $extras{$extra} = sprintf("\e[31;1mwrong hardlink target (%d:%d)\e[0m", $devsrc, $inosrc); } } else { open my $extfd, "<", $abs_extra; open my $srcfd, "<", $abs_source; local $/ = undef; if (scalar(<$extfd>) eq scalar(<$srcfd>)) { $extras{$extra} = "\e[32;1menabled\e[0m"; } else { $extras{$extra} = sprintf("\e[31;1mout of synch (re-copy)\e[0m"); } } } else { $extras{$extra} = "\e[33;1mdisabled\e[0m"; } } # Now let's add dependency info for my $extra (keys(%extras)) { next unless $extras{$extra} =~ m/enabled/; # only process enabled extras. my $abs_extra = File::Spec->catfile($abs_srcdir, "extra", $extra); my @deps = split / +/, getdependencies($abs_extra); for my $dep (@deps) { if (exists($extras{$dep})) { my $ref = \$extras{$dep}; # Take reference. if ($$ref !~ m/needed by/) { # First dependency found. if ($$ref =~ m/enabled/) { $$ref .= " (needed by \e[32;1m$extra\e[0m"; } else { $$ref =~ s/\e\[.*?m//g; # Strip out previous coloring. Will be set in bold+red+blink later. $$ref .= " (needed by \e[0;32;1;5m$extra\e[0;31;1;5m"; } } else { if ($$ref =~ m/enabled/) { $$ref .= ", \e[32;1m$extra\e[0m"; } else { $$ref .= ", \e[0;32;1;5m$extra\e[0;31;1;5m"; } } } } } 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/ ? ")" : ""); } else { printf "%-*s = %s%s\n", $maxlen, $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; } # Get dependencies, and add them to be processed. my @deps = split / +/, getdependencies($extrapath); 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") { 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"; } } } print "Enabling $extra ... \n"; symlink "extra/$extra", $source or print STDERR "$source: Cannot link to 'extra/$extra': $!\n"; } } 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 / +/, getdependencies("src/modules/extra/$file"); # 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; } } } # Now remove. print "Disabling $extra ... \n"; unlink "src/modules/$extra" or print STDERR "Cannot disable \e[32;1m$extra\e[0m : $!\n"; } }