#!/usr/bin/perl -w
use strict;
# Usage:
#
# ./darcs-all [-q] [-s] [-i] [-r repo]
# [--nofib] [--testsuite] [--checked-out] cmd [darcs flags]
#
# Applies the darcs command "cmd" to each repository in the tree.
#
# e.g.
# ./darcs-all -r http://darcs.haskell.org/ghc get
# To get any repos which do not exist in the local tree
#
# ./darcs-all -r ~/ghc-validate push
# To push all your repos to the ~/ghc-validate tree
#
# ./darcs-all pull -a
# To pull everything from the default repos
#
# ./darc-all push --dry-run
# To see what local patches you have relative to the main repos
#
# -q says to be quite, and -s to be silent.
#
# -i says to ignore darcs errors and move on to the next repository
#
# -r repo says to use repo as the location of package repositories
#
# --checked-out says that the remote repo is in checked-out layout, as
# opposed to the layout used for the main repo. By default a repo on
# the local filesystem is assumed to be checked-out, and repos accessed
# via HTTP or SSH are assumed to be in the main repo layout; use
# --checked-out to override the latter.
#
# --nofib, --testsuite also get the nofib and testsuite repos respectively
#
# The darcs get flag you are most likely to want is --complete. By
# default we pass darcs the --partial flag.
#
$| = 1; # autoflush stdout after each print, to avoid output after die
my $defaultrepo;
my $verbose = 2;
my $ignore_failure = 0;
my $want_remote_repo = 0;
my $checked_out_flag = 0;
my %tags;
# Figure out where to get the other repositories from.
sub getrepo {
my $basedir = ".";
my $repo = $defaultrepo || `cat $basedir/_darcs/prefs/defaultrepo`;
chomp $repo;
my $repo_base;
my $checked_out_tree;
if ($repo =~ /^...*:/) {
# HTTP or SSH
# Above regex says "at least two chars before the :", to avoid
# catching Win32 drives ("C:\").
$repo_base = $repo;
# --checked-out is needed if you want to use a checked-out repo
# over SSH or HTTP
if ($checked_out_flag) {
$checked_out_tree = 1;
} else {
$checked_out_tree = 0;
}
# Don't drop the last part of the path if specified with -r, as
# it expects repos of the form:
#
# http://darcs.haskell.org
#
# rather than
#
# http://darcs.haskell.org/ghc
#
if (!$defaultrepo) {
$repo_base =~ s#/[^/]+/?$##;
}
}
elsif ($repo =~ /^\/|\.\.\/|.:(\/|\\)/) {
# Local filesystem, either absolute or relative path
# (assumes a checked-out tree):
$repo_base = $repo;
$checked_out_tree = 1;
}
else {
die "Couldn't work out repo";
}
return $repo_base, $checked_out_tree;
}
sub message {
if ($verbose >= 2) {
print "@_\n";
}
}
sub warning {
if ($verbose >= 1) {
print "warning: @_\n";
}
}
sub darcs {
message "== running darcs @_";
system ("darcs", @_) == 0
or $ignore_failure
or die "darcs failed: $?";
}
sub darcsall {
my $localpath;
my $remotepath;
my $path;
my $tag;
my @repos;
my ($repo_base, $checked_out_tree) = getrepo();
open IN, "< packages" or die "Can't open packages file";
@repos = <IN>;
close IN;
foreach (@repos) {
chomp;
if (/^([^# ]+) +(?:([^ ]+) +)?([^ ]+) +([^ ]+)$/) {
$localpath = $1;
$tag = defined($2) ? $2 : "";
$remotepath = $3;
if ($checked_out_tree) {
$path = "$repo_base/$localpath";
}
else {
$path = "$repo_base/$remotepath";
}
if (-d "$localpath/_darcs") {
if ($want_remote_repo) {
darcs (@_, "--repodir", $localpath, $path);
} else {
darcs (@_, "--repodir", $localpath);
}
}
elsif ($tag eq "") {
message "== Required repo $localpath is missing! Skipping";
}
else {
message "== $localpath repo not present; skipping";
}
}
elsif (! /^(#.*)?$/) {
die "Bad line: $_";
}
}
}
sub darcsget {
my $r_flags;
my $localpath;
my $remotepath;
my $path;
my $tag;
my @repos;
my ($repo_base, $checked_out_tree) = getrepo();
if (! grep /(?:--complete|--partial)/, @_) {
warning("adding --partial, to override use --complete");
$r_flags = [@_, "--partial"];
}
else {
$r_flags = \@_;
}
open IN, "< packages" or die "Can't open packages file";
@repos = <IN>;
close IN;
foreach (@repos) {
chomp;
if (/^([^ ]+) +(?:([^ ]+) +)?([^ ]+) +([^ ]+)$/) {
$localpath = $1;
$tag = defined($2) ? $2 : "";
$remotepath = $3;
if ($checked_out_tree) {
$path = "$repo_base/$localpath";
}
else {
$path = "$repo_base/$remotepath";
}
if (($tag eq "") || defined($tags{$tag})) {
if (-d $localpath) {
warning("$localpath already present; omitting");
}
else {
darcs (@$r_flags, $path, $localpath);
}
}
}
elsif (! /^(#.*)?$/) {
die "Bad line: $_";
}
}
}
sub main {
if (! -d "compiler") {
die "error: darcs-all must be run from the top level of the ghc tree."
}
while ($#_ ne -1) {
my $arg = shift;
# We handle -q here as well as lower down as we need to skip over it
# if it comes before the darcs command
if ($arg eq "-q") {
$verbose = 1;
}
elsif ($arg eq "-s") {
$verbose = 0;
}
elsif ($arg eq "-r") {
$defaultrepo = shift;
}
elsif ($arg eq "-i") {
$ignore_failure = 1;
}
# --nofib tells get to also grab the nofib repo.
# It has no effect on the other commands.
elsif ($arg eq "--nofib") {
$tags{"nofib"} = 1;
}
# --testsuite tells get to also grab the testsuite repo.
# It has no effect on the other commands.
elsif ($arg eq "--testsuite") {
$tags{"testsuite"} = 1;
}
elsif ($arg eq "--checked-out") {
$checked_out_flag = 1;
}
else {
unshift @_, $arg;
if (grep /^-q$/, @_) {
$verbose = 1;
}
last;
}
}
if ($#_ eq -1) {
die "What do you want to do?";
}
my $command = $_[0];
if ($command eq "get") {
darcsget @_;
}
else {
if ($command =~ /^(?:w|wh|wha|what|whats|whatsn|whatsne|whatsnew)$/) {
# Hack around whatsnew failing if there are no changes
$ignore_failure = 1;
}
if ($command =~ /^(pul|pus|sen|put)/) {
$want_remote_repo = 1;
}
darcsall @_;
}
}
END {
message "== Checking for old bytestring repo";
if (-d "libraries/bytestring/_darcs") {
if ((system "darcs annotate --repodir libraries/bytestring --match 'hash 20080118173113-3fd76-d5b74c04372a297b585ebea4e16d524551ce5035' > /dev/null 2> /dev/null") == 0) {
print <<EOF;
============================
ATTENTION!
You have an old bytestring repository in your GHC tree!
Please remove it (e.g. "rm -r libraries/bytestring"), and the new
version of bytestring will be used from a tarball instead.
============================
EOF
}
}
}
main(@ARGV);