You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1408 lines
42 KiB

#!/usr/bin/perl -w
# Script to handle building KDE from CVS. You need to set
# some environment variables as appropriate. Specifically,
# QTDIR needs to be set, as well as a sane PATH.
#
# This script is designed to be run by cron, which will
# collect the output and mail it to the user who requested
# the job. For that reason, all output is sent to STDOUT.
#
# Copyright (c) 2003, 2004 Michael Pyne.
#
# You may use, alter, and redistribute this software under the terms
# of the GNU General Public License, v2 (or any later version).
#
# TODO:
# XXX: It would be better to have lockfiles in each directory as it's
# being updated, instead of having one big lock for the script.
use strict;
use warnings;
use Fcntl; # For sysopen constants
# Some global variables
# Remember kids, global variables are evil! I only get to do this
# because I'm an adult and you're not! :-P
my %global_opts = (
"checkout-only" => "",
"apply-qt-patches" => "",
"configure-flags" => "",
"qtdir" => "",
"kdedir" => "",
"cxxflags" => "",
"libpath" => "",
"do-not-compile" => "",
"binpath" => "",
"no-cvs" => "",
"refresh-build" => "",
"reconfigure" => "",
"release-tag" => "",
"build-system-only" => "",
"cvs-server" => "",
"cvs-root" => "$ENV{HOME}/kdecvs",
"lockfile" => "$ENV{HOME}/.kdecvs-lock",
# Deprecated in v0.6. Logging is important, but doesn't
# make sense to all be dumped to stdout. I may add a simple
# 'stfu' switch later, but this one's gone.
# "make-output-file" => "make",
"disable-build-list" => "",
"manual-build" => "",
"pretend" => "",
"use-unsermake" => "",
"make-options" => "",
);
my %package_opts; # Holds module-specific options.
my @update_list; # List of modules to update/checkout.
my @build_list; # List of modules to build.
my @install_list; # List of modules to install.
# Subroutine definitions
# Subroutine to handle removing the lock file upon receiving a signal
sub quit_handler
{
print "Signal received, terminating.\n";
finish(5);
}
# This subroutine returns an option value for a given module. Some
# globals can't be overridden by a module's choice. If so, the
# module's choice will be ignored, and a warning will be issued.
#
# Option names are case-sensitive!
#
# First parameter: Name of module
# Second paramenter: Name of option
sub get_option
{
my $module = shift;
my $option = shift;
# Configure flags and CXXFLAGS are appended to the global option
if (($module ne 'qt-copy' && $option eq 'configure-flags')
|| $option eq 'cxxflags')
{
my $value = $global_opts{$option};
if (exists $package_opts{$module}->{$option})
{
$value .= " $package_opts{$module}->{$option}";
}
return $value;
}
# These options can't override globals
if ($option eq "cvs-root" ||
$option eq "cvs-server" ||
$option eq "qtdir" ||
$option eq "libpath" ||
$option eq "binpath" ||
$option eq "kdedir" ||
$option eq "pretend" ||
$option eq "lockfile")
{
return $global_opts{$option};
}
# Everything else overrides the global, unless of course it's not set.
# If we're reading for global options, we're pretty much done.
if ($module eq 'global' || not exists $package_opts{$module}->{$option})
{
return $global_opts{$option};
}
else
{
return $package_opts{$module}->{$option};
}
}
# Subroutine to run a command with redirected STDOUT and STDERR. First parameter
# is name of the log file (relative to the log directory), and the
# second parameter is a reference to an array with the command and
# its arguments
sub log_command
{
my $kdecvs = get_option('global', 'cvs-root');
my $pid;
my $filename = shift;
my @command = @{(shift)};
if (get_option('global', 'pretend'))
{
print "Would have run ", join (' ', @command), "\n";
return 0;
}
if ($pid = fork)
{
# Parent
waitpid $pid, 0;
return $?;
}
else
{
# Child
if (not -e "$kdecvs/log")
{
return 1 if not super_mkdir ("$kdecvs/log");
}
# Redirect stdout and stderr to the given file.
open (STDOUT, ">$kdecvs/log/$filename");
open (STDERR, ">&STDOUT");
exec (@command);
}
}
# Subroutine to run the make command with the arguments given by the
# passed list. If make-output-file is set, the output of STDOUT and
# STDERR will be redirected to that file. The first argument of the
# list given must be the module that we're making.
sub safe_make (@)
{
my $module = shift;
my $kdecvs = get_option('global', 'cvs-root');
my $opts = get_option($module, 'make-options');
# Add make-options to the given options
unshift (@_, split(/\s/, $opts));
if (get_option('global', 'pretend'))
{
$opts = join('.', @_);
print "Would have run make $opts > $kdecvs/log/$module-build\n";
return 0;
}
return log_command ("$module-build", ['make', @_] );
}
# Subroutine to add a variable to the environment, but ONLY if it
# is set. First parameter is the variable to set, the second is the
# value to give it.
sub setenv
{
my $var = shift;
my $val = shift;
my $pretend = get_option ('global', 'pretend');
return unless $val;
if (not $pretend)
{
$ENV{$var} = $val;
}
else
{
print "Would have set $var=$val.\n";
}
}
# Display an error message to the user regarding their relative lack of
# ~/.kdecvs-buildrc, and point them to some help.
sub dead_whine
{
print <<"HOME";
Unable to open $ENV{HOME}/.kdecvs-buildrc!
$!
This file is necessary as it contains defintions for \$CVSROOT, among
other important variables!
For information on the format of .kdecvs-buildrc or for a sample file,
visit http://grammarian.homelinux.net/kdecvs-build/
HOME
finish (1); # We can't go on!
}
# This subroutine reads in the settings from the user's configuration
# file.
sub read_options
{
# The options are stored in $ENV{HOME}/.kdecvs-buildrc
my $config_file = "$ENV{HOME}/.kdecvs-buildrc";
open CONFIG, "<$config_file" or dead_whine();
my ($option, $flags, $modulename);
# Read in global settings
OUTER: while (<CONFIG>)
{
s/#.*$//; # Remove comments
next if (/^\s*$/); # Skip blank lines
# First command in .kdecvs-buildrc should be a global
# options declaration, even if none are defined.
if (not /^global\s*$/)
{
print "Invalid configuration file $config_file.\n";
print "Expecting global settings section!\n";
finish(1);
}
# Now read in each global option
while (<CONFIG>)
{
s/#.*$//; # Remove comments
next if /^\s*$/; # Skip blank lines
last OUTER if /^end\s+global/; # Stop
# The option is the first word, followed by the
# flags on the rest of the line. The interpretation
# of the flags is dependant on the option.
($option, $flags) = /^\s*([-a-zA-Z0-9]+)\s+(.*)$/;
# Replace tildes with home directory.
1 while ($flags =~ s/(^|:)~/$1$ENV{'HOME'}/);
$global_opts{$option} = $flags;
}
}
# Now read in module settings
while (<CONFIG>)
{
s/#.*$//; # Remove comments
next if (/^\s*$/); # Skip blank lines
if (not /^module\s+[-\w]+\s*$/)
{
print "Invalid configuration file $config_file!\n";
print "Expecting a start of module section.\n";
print "Global settings will be retained.\n";
}
# Get modulename
($modulename) = /^module\s+([-\w]+)\s*$/;
$package_opts{$modulename} = { }; # Set up defaults
while (<CONFIG>)
{
s/#.*$//; # Remove comments
next if (/^\s*$/); # Skip blank lines
last if (/^end\s+module/);
# Split into option and its flags.
($option, $flags) = /^\s*([-a-zA-Z0-9]+)\s+(.*?)\s*$/;
# Replace tildes with home directory.
1 while ($flags =~ s/(^|:)~/$1$ENV{'HOME'}/);
$package_opts{$modulename}->{$option} = $flags;
}
# Done reading options, add this module to the update list
push (@update_list, $modulename);
# Add it to the build list, unless the build is only
# supposed to be done manually.
if (not get_option ($modulename, 'manual-build'))
{
push (@build_list, $modulename);
}
}
close CONFIG;
}
# Subroutine to initialize some environment variable for building
# KDE from CVS. Change this section if a dependency changes later.
sub initialize_environment
{
$ENV{"WANT_AUTOMAKE"} = "1.7";
$ENV{"WANT_AUTOCONF_2_5"} = "1";
$ENV{"PATH"} = get_option ('global', 'binpath');
my $cvsserver = get_option ('global', 'cvs-server');
$ENV{'CVS_RSH'} = 'ssh' if $cvsserver =~ /^:ext:/;
}
# Subroutine to process the command line arguments. Any arguments so
# processed will be removed from @ARGV.
# The following arguments are recognized.
# --version Print the version
# --author Print the author
# --help Print some help
# --pretend Don't really perform any actions, just say would we would do.
# --install Install all packages that have built successfully,
# if it is the only command-line option. If it isn't, the
# following entries on the command-line are assumed to be
# packages to install, and we install only those.
sub process_arguments
{
my $arg;
my $author = "Michael Pyne <mpyne\@grammarian.homelinux.net>\n";
my $version = "kdecvs-build 0.60\n";
my @argv;
while ($_ = shift @ARGV)
{
SWITCH: {
/^--version$/ && do { print $version; finish(); };
/^--author$/ && do { print $author; finish(); };
/^--help$/ && do {
print <<DONE;
kdecvs-build version $version
This script automates (well, attempts to :-) ) the download, build,
and install process for KDE CVS.
You must first setup a .kdecvs-buildrc file in your home directory.
Please visit http://grammarian.homelinux.net/kdecvs-build/ for
information on how to write the file. There is also a simple GUI for
creating the file, which you can find at the above site.
Anyways, after setting up .kdecvs-buildrc, you can run this program
from either the command-line or from cron. It will automatically download
the modules from CVS, create the build system, and configure and make
the modules you tell it too. If you\'d like, you can use this program to
install KDE as well, if you\'re building KDE for a single user.
Basic synopsis, after setting up .kdecvs-buildrc:
\$ kdecvs-build [package names] (Download and make KDE from CVS)
\$ kdecvs-build --install [package names] (Install single-user KDE)
If you don\'t specify any particular package names, then your settings
in .kdecvs-buildrc will be used. If you DO specify a package name, then
your settings will still be read, but the script will try to build/install
the package regardless of .kdecvs-buildrc
Copyright (c) 2003, 2004 Michael Pyne <mpyne\@grammarian.homelinux.net>
The script is distributed under the terms of the GNU General Public License
v2, and includes ABSOLUTELY NO WARRANTY!!!
Options:
--no-cvs Skip contacting the CVS server.
--no-build Skip the build process.
--pretend (or -p) Don't actually contact the CVS server, run make,
or create/delete files and directories. Instead,
output what the script would have done.
--refresh-build Start the build from scratch.
--reconfigure Run configure again, but don't clean the build
directory or re-run make -f Makefile.cvs.
--build-system-only Create the build infrastructure, but don't actually
perform the build.
--install Try to install the packages passed on the command
line, or all packages in ~/.kdecvs-buildrc that don't
have manual-build set.
--help You\'re reading it. :-)
--author Output the author(s)\'s name.
--version Output the program version.
DONE
finish();
};
/^--install$/ && do {
if ($#ARGV > -1)
{
@install_list = @ARGV;
@ARGV = ();
}
else
{
# Get list of built items from $kdecvs/build/successfully-built
my $kdecvs = get_option ('global', 'cvs-root');
if (not open BUILTLIST, "<$kdecvs/build/successfully-built")
{
print "Can't determine what modules have built. You must\n";
print "specify explicitly on the command line.\n";
finish (1);
}
@install_list = <BUILTLIST>;
chomp (@install_list);
close BUILTLIST;
}
last SWITCH;
};
/^--no-cvs$/ && do {
$global_opts{'no-cvs'} = 1;
last SWITCH;
};
/^--reconfigure$/ && do {
$global_opts{'reconfigure'} = 1;
last SWITCH;
};
/^--no-build$/ && do {
$global_opts{'manual-build'} = 1;
@build_list = ();
last SWITCH;
};
/^--build-system-only$/ && do {
$global_opts{'build-system-only'} = 1;
last SWITCH;
};
/^(--pretend)|(-p)$/ && do {
$global_opts{'pretend'} = 1;
last SWITCH;
};
/^--refresh-build$/ && do {
$global_opts{'refresh-build'} = 1;
last SWITCH;
};
/^-/ && print "WARNING: Unknown option $_\n";
# Fall through on purpose
push @argv, $_; # Reconstruct correct @ARGV
}
}
@ARGV = @argv;
}
# Subroutine to try to get a lock on the script's lockfile to prevent
# more than one script from updating KDE CVS at once.
# The value returned depends on the system's open() call. Normally 0
# is failure and non-zero is success (e.g. a file descriptor to read).
sub get_lock
{
my $lockfile = get_option ("global", "lockfile");
my $result = sysopen LOCKFILE, $lockfile, O_WRONLY | O_CREAT | O_EXCL;
# Very wordy way of saying to return if result == 0
return $result unless $result;
# Install signal handlers to ensure that the lockfile gets closed.
# There is a race condition here, but at worst we have a stale lock
# file, so I'm not *too* concerned.
$SIG{'HUP'} = \&quit_handler;
$SIG{'INT'} = \&quit_handler;
$SIG{'QUIT'} = \&quit_handler;
$SIG{'ABRT'} = \&quit_handler;
$SIG{'TERM'} = \&quit_handler;
}
# Subroutine to free the lock allocated by get_lock()
sub close_lock
{
my $lockfile = get_option ('global', "lockfile");
close LOCKFILE;
unlink $lockfile;
}
# Subroutine to get the list of CVS modules to update. Returned
# as a list. Parse the command-line arguments first.
sub get_update_list
{
my @super_list = @ARGV;
@super_list = @update_list if ($#ARGV == -1);
# Check to see if the user has requested that one of the modules
# use unsermake. If so, we need to check if kdenonbeta is already
# supposed to be checked out. If so, we need to make sure that
# unsermake is present in any checkout-only directives, and if not,
# we need to add kdenonbeta/unsermake to the checkout list.
if (scalar grep (get_option ($_, 'use-unsermake'), @super_list))
{
if (scalar grep (/^kdenonbeta$/, @super_list) == 0)
{
# kdenonbeta isn't being downloaded by the user.
unshift (@super_list, 'kdenonbeta');
$package_opts{'kdenonbeta'} = {
'manual-build' => 'true',
'checkout-only' => 'unsermake',
};
}
elsif (get_option ('kdenonbeta', 'checkout-only') and
get_option ('kdenonbeta', 'checkout-only') !~ /\bunsermake\b/)
{
# kdenonbeta is being checked out, but the user has
# exclused unsermake.
$package_opts{'kdenonbeta'}->{'checkout-only'} .= " unsermake";
}
}
# Search each entry in the update list to see if someone has asked for
# a partial checkout.
if (scalar grep (get_option ($_, 'checkout-only'), @super_list))
{
# A module is specified for piecewise checkout. Add kde-common
# if it's not already in the list.
if (scalar grep (/^kde-common$/, @super_list) == 0)
{
# kde-common isn't here, add it to beginning.
unshift (@super_list, "kde-common");
# Add entry for it to package_opts as well.
$package_opts{'kde-common'} = {
'manual-build' => 'true',
};
}
}
return @super_list;
}
# Subroutine to get the list of CVS modules to build. Returned
# as a list. A module will not be built if manual-build is set
# in the module's options. The command-line arguments should have been
# parsed first.
sub get_build_list
{
return () if get_option('global', 'manual-build');
return @build_list if $#ARGV == -1;
# Default list
return @ARGV;
}
# Helper subroutine for debugging purposes. Dumps all of the
# options which have been read in to %global_opts and %package_opts.
sub dump_options
{
my ($item, $ref_item, $ref);
foreach $item (keys %global_opts)
{
print "Global option $item is $global_opts{$item}\n";
}
foreach $item (keys %package_opts)
{
print "\nOptions for module $item:\n";
foreach $ref_item (keys %{$package_opts{$item}})
{
print "Option $ref_item is $package_opts{$item}{$ref_item}\n";
}
}
}
# Subroutine to unlink the given symlink if global-pretend isn't set.
sub safe_unlink
{
if (get_option ("global", 'pretend'))
{
print "Would have unlinked ", shift, ".\n";
return 1; # Return true
}
return unlink (shift);
}
# Subroutine to execute the system call on the given list if the pretend
# global option is not set.
sub safe_system(@)
{
if (not get_option ("global", "pretend"))
{
print "Executing ", join(" ", @_), "\n";
return system (@_) >> 8;
}
print "Would have run ", join(' ', @_), ".\n";
return 0; # Return true
}
# Helper subroutine to create a directory, including any parent
# directories that may also need created.
# Returns 0 on failure, non-zero on success
sub super_mkdir
{
my $pathname = shift;
my $temp;
my @parts = split (/\//, $pathname);
if (get_option ("global", "pretend"))
{
print "Would have created $pathname\n";
return 1;
}
foreach (@parts)
{
$temp .= "$_/";
next if -e $temp;
return 0 if not mkdir ($temp);
}
return 1;
}
# Subroutine to remove a package from the package build list. This
# is for use when you've detected an error that should keep the
# package from building, but you don't want to abort completely.
sub dont_build
{
my $module = shift;
# Weed out matches of the module name
@build_list = grep (!/^$module$/, @build_list);
}
# Subroutine to checkout a CVS module, but to do so non-recursively.
# The first parameter should be the CVS module to check out.
# The second parameter should be the directory within the module to
# checkout.
#
# This subroutine handles one directory within the module at a time.
#
# It is important to remember that the admin directory is special. In
# this program, admin is added automatically to the list of directories
# to install when it is needed.
#
# Returns 0 on success, non-zero on failure.
#
# whenever a module is checked out piecewise.
sub checkout_cvs_partial_dir
{
my $module = shift;
my $dir = shift;
my $recurse = shift;
my @args;
my $kdecvs = get_option ("global", "cvs-root");
my $cvsroot = get_option ("global", "cvs-server");
print ("Checking out $module/$dir.\n");
chdir ("$kdecvs");
if ($dir eq "admin")
{
# In KDE CVS, the admin directory is a special sub-directory
# that doesn't exist in any module. Instead, it is linked to
# kde-common/admin. Normally KDE CVS updates that automatically,
# but when you update piecewise, you need to do it yourself.
if (not -e "kde-common/admin")
{
print "You haven't checked out kde-common! It must be checked\n";
print "out prior to this module ($module) since you are\n";
print "performing a piecewise installation.\n";
return 1;
}
safe_unlink ("$kdecvs/$module/admin");
return safe_system('ln', '-s', "$kdecvs/kde-common/admin", "$module/$dir");
}
else
{
@args = ('cvs', "-d$cvsroot");
push @args, (-e "$module/$dir") ? 'up' : 'co';
push @args, '-l' unless $recurse;
push @args, '-r', get_option($module, "release-tag") if (get_option($module, "release-tag"));
push @args, "$module/$dir";
}
my $fname = "$module/$dir"; # $dir may itself contain slashes
$fname =~ s/\//-/g;
return run_cvs (\@args, $fname);
}
# Subroutine to check out a specific set of directories from a module,
# instead of recursively checking out the entire module.
# The first parameter is the module to check out. The subroutine will
# automatically pull the list of directories to checkout from %package_opts.
# Only call the subroutine if the module has set a value to checkout-only.
sub checkout_cvs_partial
{
my $module = shift;
my @dirlist = split (/\s+/, get_option ($module, 'checkout-only'));
my @args;
my $kdecvs = get_option ('global', 'cvs-root');
my $cvsroot = get_option ('global', 'cvs-server');
my $result = 0;
my $item;
chdir ($kdecvs);
# Check if the user specified the admin subdirectory. If not,
# add it.
push (@dirlist, 'admin') if scalar grep (/^admin$/, @dirlist) == 0;
# Check out the module base.
@args = ('cvs', "-d$cvsroot");
push @args, (-e "$kdecvs/$module") ? 'up' : 'co', '-l';
push @args, '-r', get_option($module, "release-tag") if get_option($module, 'release-tag');
push @args, $module;
if (run_cvs (\@args, "$module-base-cvs"))
{
print "Error trying to partially checkout $module!\n$!\n";
print "The module will be blocked from building.\n";
dont_build ($module);
return 1;
}
ITEM_LOOP: for $item (@dirlist)
{
# We need to split each item in this list into its respective directories.
# For example, we may be checking out kdenonbeta/applets/ksearchapplet. We
# need to (non-recursively) download kdenonbeta/applets, and then
# (recursively) kdenonbeta/applets/ksearchapplet. This is because of stuff
# like the Makefile.am files that are laying around.
my @dir_pieces = split('/', $item);
my $piece = shift @dir_pieces;
while (scalar (@dir_pieces))
{
# Don't recurse, we have more pieces.
if (checkout_cvs_partial_dir ($module, $piece, 0))
{
print "Unable to check out $module/$piece!\n";
print "Module $module will be blocked from building.\n";
dont_build ($module);
$result = 1;
next ITEM_LOOP;
}
$piece = join ('/', $piece, shift @dir_pieces);
}
# Recurse here, we're finished with prior dirs.
if (checkout_cvs_partial_dir ($module, $piece, 1))
{
print "Unable to check out $module/$piece!\n";
print "Module $module will be blocked from building.\n";
dont_build ($module);
$result = 1;
next;
}
}
return $result;
}
# Subroutine to ensure that the user has a cvs configuration. If not, one
# similar to the recommended version on developer.kde.org will be installed.
sub check_cvs_config
{
my $cvsroot = get_option ('global', 'cvs-server');
if (not -e "$ENV{HOME}/.cvsrc")
{
print "You do not seem to have a .cvsrc. Now creating a default... ";
open CVSRC, "> $ENV{HOME}/.cvsrc";
print CVSRC "cvs -z4 -q\n";
print CVSRC "diff -u3 -p\n";
print CVSRC "update -dP\n";
print CVSRC "checkout -P\n";
close CVSRC;
print "Done\n";
}
if (not -e "$ENV{HOME}/.cvspass")
{
# We need to login. We could use the Expect module to
# simulate a user login, but that would add another
# dependency for something which is really quite dumb.
# If the user doesn't login, then they will see warnings,
# but that should be it.
print <<EOF;
You need to login to CVS to avoid warnings about a missing .cvspass file!
At the command prompt, type \"cvs -d$cvsroot login\" without the quotes
and hit Enter. Then hit Enter again (to indicate a blank password) when
the prompt asks for your password.
EOF
}
}
# Subroutine to update a list of CVS modules. The first
# parameter is a reference of a list of the modules to update.
# If the module has not already been checkout out, this subroutine
# will do so for you.
#
# Returns 0 on success, non-zero on error.
sub handle_updates
{
my $update_ref = shift;
my $kdecvs = get_option ('global', 'cvs-root');
my $cvsroot = get_option ('global', 'cvs-server');
my $result = 0;
my $module;
# No reason to print out the text if we're not doing anything.
return 0 if get_option ('global', 'no-cvs');
check_cvs_config();
print "<<< UPDATING CVS DIRECTORIES >>>\n\n";
if (not -e $kdecvs)
{
print "KDE CVS download directory doesn't exist, creating.\n";
if (not super_mkdir ($kdecvs))
{
print "Unable to make directory $kdecvs!\n$!\n";
@build_list = (); # Clear out the build list, since we can't build.
@install_list = (); # Can't install either.
return 1;
}
}
foreach $module (@{$update_ref})
{
if (not exists $package_opts{$module})
{
print "Unknown module $module, configure it in ~/.kdecvs-buildrc.\n";
next;
}
next if get_option($module, 'no-cvs');
my $command;
my $verb;
chdir ("$kdecvs");
if (-e "$kdecvs/$module/CVS")
{
# The CVS directory already exists, so it has probably already been
# checked out.
print "Updating $module\n";
$verb = 'updating';
$command = 'up';
}
else
{
print "Checking out $module\n";
$verb = 'checking out';
$command = 'co';
}
if (get_option($module, 'checkout-only'))
{
# Don't check out the entire module, merely the
# parts the user wants
$result = 1 if checkout_cvs_partial ($module);
next;
}
my @args = ('cvs', "-d$cvsroot", $command);
push @args, '-r', get_option($module, "release-tag") if get_option($module, "release-tag");
push @args, $module;
if (run_cvs(\@args, "$module-cvs-$command"))
{
print "Error $verb $module, removing from list of packages to build.\n";
dont_build ($module);
$result = 1;
}
print "\n";
}
print "<<< UPDATE COMPLETE >>>\n";
return $result;
}
# Subroutine to run the qt-copy apply_patches script. Assumes we're
# already in the right directory.
sub safe_apply_patches
{
my $kdecvs = get_option('global', 'cvs-root');
if (get_option('global', 'pretend'))
{
print "Would have run ./apply_patches\n";
return 0;
}
print "Applying recommended Qt patches.\n";
chdir ("$kdecvs/qt-copy");
return (system("./apply_patches") >> 8);
}
# Subroutine to run and log the configure command. First parameter is the
# path to the configure script to run, the second parameter is a scalar
# containing all of the configure flags to apply
sub safe_configure
{
my $kdecvs = get_option('global', 'cvs-root');
my $module = shift;
my $script = "$kdecvs/$module/configure";
if (get_option('global', 'pretend'))
{
print "Would have configured the module.\n";
return 0;
}
my @commands = split (/\s+/, get_option($module, 'configure-flags'));
# Get the user's CXXFLAGS
my $cxxflags = get_option ($module, 'cxxflags');
setenv ('CXXFLAGS', $cxxflags);
setenv ('DO_NOT_COMPILE', get_option ($module, 'do-not-compile'));
if ($module ne 'qt-copy')
{
my $kdedir = get_option ('global', 'kdedir');
push @commands, "CXXFLAGS=$cxxflags" if $cxxflags;
push @commands, "--prefix=$kdedir";
}
print "Running configure...\n";
unshift @commands, $script;
return log_command("$module-configure.log", \@commands);
}
# Subroutine to create the build system for a module. This involves making
# sure the directory exists and then running make -f Makefile.cvs. This
# subroutine assumes that the module is already downloaded.
sub safe_create_build_system
{
my $kdecvs = get_option('global', 'cvs-root');
my $module = shift;
if (get_option('global', 'pretend'))
{
print "Would have created $module\'s build system.\n";
return 0;
}
chdir ("$kdecvs/$module");
if (log_command ("$module-build-system", [ "make", "-f", "Makefile.cvs" ]))
{
print "Unable to create build system for $module\n";
return 1;
}
return 0;
}
sub needs_refreshed
{
my $kdecvs = get_option ('global', 'cvs-root');
my $module = shift;
# qt-copy doesn't use the build directory
if ($module eq 'qt-copy')
{
# This file only exists after the configure script has been run.
return (not -e ("$kdecvs/qt-copy/src/tools/qconfig.cpp"));
}
return 1 if ((not -e "$kdecvs/build/$module") ||
(-e "$kdecvs/build/$module/.refresh-me") ||
get_option($module, "refresh-build") ||
(not -e "$kdecvs/build/$module/Makefile"));
return 0;
}
sub run_cvs
{
my $arg_ref = shift;
my $logfilename = shift;
my %hash_count;
my $kdecvs = get_option ('global', 'cvs-root');
my $result;
# Do cvs update.
$result = log_command($logfilename, $arg_ref);
# There will be no result if we're pretending, so don't even
# bother.
return 0 if (get_option ('global', 'pretend'));
$logfilename = "$kdecvs/log/$logfilename";
# We need to open the file and try to determine what the CVS process
# did.
open CVS_LOG, "<$logfilename";
while (<CVS_LOG>)
{
# Count updates and patches together.
$hash_count{'updated'}++ if /^U/;
$hash_count{'updated'}++ if /^P/;
$hash_count{'added'}++ if /^A/;
$hash_count{'removed'}++ if /^R/;
$hash_count{'modified'}++ if /^M/;
$hash_count{'conflicted'}++ if /^C/;
}
close CVS_LOG;
my %endings = (
'updated' => 'files were updated',
'1updated' => 'file was updated',
'added' => 'files were added',
'1added' => 'file was added',
'removed' => 'files were removed',
'1removed' => 'file was removed',
'modified' => 'files were modified',
'1modified' => 'file was modified',
'conflicted' => 'files had conflicts',
'1conflicted' => 'file had conflicts',
);
my ($key, $value);
while (($key, $value) = each %hash_count)
{
next unless $value > 0;
my $ending_key = $value > 1 ? $key : ('1' . $key);
my $ending = $endings{$ending_key};
print "$value $ending.\n";
}
return $result;
}
# Subroutine to setup the build system in a directory. The first parameter
# is the module name. Returns boolean true on success, boolean false (0)
# on failure.
sub setup_build_system
{
my $module = shift;
my $kdecvs = get_option ('global', 'cvs-root');
my $do_configure = get_option ($module, 'reconfigure');
if (needs_refreshed($module))
{
# The build system needs created, either because it doesn't exist, or
# because the user has asked that it be completely rebuilt.
print "Preparing build system for $module.\n";
# Remove directory if it already exists.
if (-e "$kdecvs/build/$module" &&
safe_system ('rm', '-rf', "$kdecvs/build/$module"))
{
print "Unable to unlink $kdecvs/build/$module, skipping.\n";
return 0; # False for this function.
}
# Clean qt-copy separately
if ($module eq 'qt-copy')
{
chdir ("$kdecvs/qt-copy");
unlink ("$kdecvs/qt-copy/.qmake.cache");
log_command ('qt-copy-clean', ['make', 'clean']);
}
# Now create the directory
if (not super_mkdir ("$kdecvs/build/$module"))
{
print "Unable to create directory $kdecvs/build/$module, skipping.\n";
return 0;
}
# Note this is the checkout directory, not the build directory
chdir ("$kdecvs/$module");
# Update the PATH and other important environment variables.
update_module_environment ($module);
# Run KDE's build preparation script if configure doesn't exist,
# or all the time if refresh-build is set.
if ((not -e "$kdecvs/$module/configure") ||
get_option($module, 'refresh-build'))
{
if (safe_create_build_system ($module))
{
print "Unable to create configure system from checkout.\n";
return 0;
}
}
$do_configure = 1;
if (($module eq "qt-copy") && get_option($module, 'apply-qt-patches'))
{
# Run apply-patches script
return 0 if safe_apply_patches ();
}
# Check to see if we're supposed to stop here
return 1 if get_option ($module, 'build-system-only');
}
if ($do_configure)
{
# Now we're in the checkout directory
# So, switch to the build dir.
# qt-copy doesn't use this metaphor, however.
chdir ("$kdecvs/$module");
chdir ("$kdecvs/build/$module") unless $module eq 'qt-copy';
# configure the module
if (safe_configure ($module))
{
print "Unable to configure $module!\n";
return 0;
}
}
return 1;
}
# Subroutine to setup the environment for a module. First parameter is the name of
# the module to set the environment for
sub update_module_environment
{
my $module = shift;
my $kdecvs = get_option ('global', 'cvs-root');
my $kdedir = get_option ($module, 'kdedir');
my $qtdir = get_option ($module, 'qtdir');
my $path = join(':', "$qtdir/bin", "$kdedir/bin", get_option ($module, 'binpath'));
my $libdir = join(':', "$qtdir/lib", "$kdedir/lib", get_option ($module, 'libpath'));
# Set up the children's environment. We use setenv since it
# won't set an environment variable to nothing. (e.g, setting
# QTDIR to a blank string might confuse Qt or KDE.
# Remove leading and trailing colons, just in case.
# Also remove more than one colon.
for ($path, $libdir)
{
s/^:*//;
s/:*$//;
s/:+/:/;
}
setenv ('LD_LIBRARY_PATH', $libdir);
setenv ('PATH', $path);
setenv ('KDEDIR', $kdedir);
setenv ('QTDIR', $qtdir);
# Everyone loves unsermake. It's a pity that not every module will compile with it.
# Benjamin Meyer has an excellent article about speeding up distributed builds using
# unsermake. You should notice a much faster build even with only one CPU, however.
if (get_option ($module, "use-unsermake"))
{
setenv ("UNSERMAKE", "$kdecvs/kdenonbeta/unsermake/unsermake");
}
# Qt has several defines of its own. Special case qt-copy for this
# reason.
setenv ("YACC", 'byacc -d') if ($module eq "qt-copy");
}
# Subroutine to handle the build process.
# First parameter is a reference of a list containing the packages
# we are to build.
# If the packages are not already checked-out and/or updated, this
# subroutine WILL NOT do so for you.
#
# This subroutine assumes that the $kdecvs directory has already been
# set up. It will create $kdecvs/build if it doesn't already exist.
#
# If $kdecvs/build/$module/.refresh-me exists, the subroutine will
# completely rebuild the module.
#
# Returns 0 for success, non-zero for failure.
sub handle_build
{
my @fail_list;
my @build_done;
my $build_ref = shift;
my $kdecvs = get_option ('global', 'cvs-root');
my $cvsroot = get_option ('global', 'cvs-server');
my $module;
# No reason to print building messages if we're not building.
return 0 if (scalar (@{$build_ref}) == 0);
print "\n<<< BUILD PROCESS >>>\n";
# Make sure the build directory is set up.
if (not -e "$kdecvs/build")
{
print "$kdecvs/build doesn't exist, creating.\n";
if (not super_mkdir ("$kdecvs/build"))
{
print "Unable to create $kdecvs/build!\n$!\n";
return 1;
}
}
foreach $module (@{$build_ref})
{
if (not exists $package_opts{$module})
{
print "Unknown module $module, configure it in ~/.kdecvs-buildrc.\n";
next;
}
next if get_option ($module, 'manual-build');
update_module_environment ($module);
# Ensure that the build system is ready.
if (not setup_build_system($module))
{
push @fail_list, $module;
next;
}
if (get_option ($module, 'build-system-only'))
{
push @build_done, $module;
next;
}
# qt-copy gets built in its source directory.
chdir ("$kdecvs/qt-copy");
chdir ("$kdecvs/build/$module") unless $module eq 'qt-copy';
print "Building $module\n";
if (safe_make ($module))
{
# Build failed
print "\nUnable to build $module!\n";
push @fail_list, $module;
}
else
{
# Build succeeded
push @build_done, $module;
}
}
print "<<< BUILD DONE >>>\n";
print "\n<<< PACKAGES SUCCESSFULLY BUILT >>>\n";
if (not get_option('global', 'disable-build-list') and
not get_option ('global', 'pretend'))
{
# Print out results, and output to a file
open BUILT_LIST, ">$kdecvs/build/successfully-built";
foreach $module (@build_done)
{
print "$module\n";
print BUILT_LIST "$module\n";
}
close BUILT_LIST;
}
else
{
# Just print out the results
print (join ("\n", @build_done), "\n");
}
if (scalar @fail_list > 0)
{
print "\n<<< PACKAGES FAILED BUILDING >>>\n";
print join ("\n", @fail_list), "\n";
}
return ((scalar @fail_list) > 0) ? 1 : 0;
}
# Subroutine to exit the script cleanly, including removing any
# lock files created. If a parameter is passed, it is interpreted
# as an exit code to use
sub finish
{
my $exitcode = shift;
$exitcode = 0 unless $exitcode;
close_lock();
exit $exitcode;
}
# Subroutine to handle the installation process. Simply calls
# 'make install' in the directory.
sub handle_install
{
my $kdecvs = get_option ('global', 'cvs-root');
my $pretend = get_option ('global', 'pretend');
my $result = 0;
for my $module (@install_list)
{
if ($module eq "qt-copy")
{
print "qt-copy doesn't need installed.\n";
next;
}
if (not exists $package_opts{$module})
{
print "Unknown module $module, configure it in ~/.kdecvs-buildrc.\n";
next;
}
if (not -e "$kdecvs/build/$module" ||
not -e "$kdecvs/build/$module/Makefile")
{
print "The build system doesn't exist for $module.\n";
print "Therefore, we can't install it. :-(.\n";
next;
}
chdir ("$kdecvs/build/$module");
if ($pretend)
{
print "Would have installed $module\n";
next;
}
# Just in case, I guess.
print "Installing $module\n";
update_module_environment ($module);
if (log_command ("$module-install", [ 'make', 'install' ]))
{
print "Unable to install $module!\n";
$result = 1;
}
}
return $result;
}
# Script starts.
read_options();
initialize_environment();
process_arguments();
if (not get_lock())
{
print "$0 is already running!\n";
exit 0; # Don't finish(), it's not our lockfile!!
}
@update_list = get_update_list();
@build_list = get_build_list();
my $time = localtime;
my $result;
print "Script started processing at $time\n";
if ($#install_list == -1)
{
# No packages to install, we're in build mode
$result = handle_updates (\@update_list);
$result = handle_build (\@build_list) unless $result;
}
else
{
# Installation mode. Check to make sure nothing's
# being updated.
if ($#ARGV > 0)
{
# We have packages specified on the command line.
print "Parameters ignored because we are installing:\n\t",
join (', ', @ARGV), "\n";
}
$result = handle_install ();
}
$time = localtime;
print "Script finished processing at $time\n";
finish($result);
# vim: set et sw=4 ts=4: