#!/usr/bin/perl
#
# Program to deal with "componentware" development environments.
# Usage:
# perl syncup.pl [-c component_list] [-a component_list] [-u username] [-p] [-n]
# -a component_list list of all components
# -c component_list list of what you want to be at the tip revision of...
# -u username name of user running this (defaults to login name)
# -n don't really update client
# -p partial update - only update the '-c' components, not everything
#
# You can set $ALL_COMPONENTS to avoid having to give "-a component_list", and
# $MY_COMPONENTS to avoid giving a "-c component_list" option.
#
# How it works:
# For each of the components in the list, let's say it's components (a,b,c,..i),
# we look for a label of the form "COMPONENTNAME_lastregressed". (You can force
# it to use a different label name for a given component, by setting $A_LABEL or
# $B_LABEL or.... (for components "a" and "b" and so on).
# 1. You must give *all* components you're working with in the "-a" option,
# including those you gave in the '-c' option;
# 2. All components must have a label that you'll be syncing to - either the
# default ("COMPONENTNAME_lastregressed") or "$COMPONENTNAME_LABEL".
# 3. Those labels must have "View:" sections that specify the lower-level
# directories in which the files live - the default "//depot/..." view
# isn't specific enough. (This is a fatal error if you made this mistake
# on the label whose component will get sync'ed to the head revision.)
# It creates a temporary label that is the union of all the component labels
# you want, adds in the HEAD REVISION for those components you specified using
# the "-c" option, and runs 'p4 sync' to that.
#
require "getopts.pl";
$UpdateAllFiles = 1;
local($AllComponentString, $MyComponentString) = ();
local(%AllComponents, %MyComponents) = ();
local(@AllButMine) = ();
local($a,$m) = ();
#
# Generic argument processing....
#
#
do Getopts("pc:a:u:n");
$UpdateAllFiles = 0 if ($opt_p ne "");
$minus_n = "-n" if ($opt_n ne "");
$AllComponentString = $opt_a;
$AllComponentString = $ENV{ALL_COMPONENTS}
if ($AllComponentString eq "" && defined($ENV{ALL_COMPONENTS}));
die "No components specified with '-a' or in \$ALL_COMPONENTS\n"
if ($AllComponentString eq "");
$p4user = $opt_u;
$p4user = $ENV{P4USER}
if ($p4user eq "" && defined($ENV{P4USER}));
$p4user = getlogin()
if ($p4user eq "");
die "No user specified with '-u' or in \$P4USER\n"
if ($p4user eq "");
$MyComponentString = $opt_c;
$MyComponentString = $ENV{MY_COMPONENTS}
if ($MyComponentString eq "" && defined($ENV{MY_COMPONENTS}));
die "No components specified with '-c' or in \$MY_COMPONENTS\n"
if ($MyComponentString eq "");
#
# ------------------------------ end of option processing ------------------------------
#
#
# Fold the options into usable forms, and look for '-c' components that aren't
# in the '-a' list.
#
foreach $a (split(/[\s,:]+/, $AllComponentString)) {
$AllComponents{$a}++;
}
foreach $m (split(/[\s,:]+/, $MyComponentString)) {
$MyComponents{$m}++;
next if defined($AllComponents{$m});
warn "Error: $m doesn't appear on list of 'all components'.\n";
$nerrs++;
}
die "$nerrs found. Exiting\n" if ($nerrs > 0);
foreach $a (sort keys %AllComponents) {
push(@AllButMine, $a)
unless($MyComponents{$a} > 0);
}
#
# At this point, the list @AllButMine is the list of components we'll need to get
# to a particular label.
#
print "Fetching head revision of: " . join(',', sort keys %MyComponents) . "\n";
print "Fetching labeled revision of: " . join(',', @AllButMine) . "\n";
# Now, some basic Perforce sanity checks....
$p4 = "p4"; $tmpname = ".tmp"; $tmplabel = "$p4user" . "_tmp";
%LabelList = ReadInLabels("$p4 labels");
foreach $a (sort keys AllComponents) {
my($upper_a);
my($TestLabelName);
($upper_a = $a) =~ tr/a-z/A-Z/;
if (defined($ENV{$upper_a . "_LABEL"})) {
$TestLabelName = $ENV{$upper_a . "_LABEL"};
} else {
$TestLabelName = $a . "_lastregressed";
}
push(@LabelsToGet, $TestLabelName)
unless($MyComponents{$a} > 0);
next if defined($LabelList{$TestLabelName});
warn "$TestLabelName does not exist.\n";
$nerrs++;
}
die "$nerrs found. Exiting\n" if ($nerrs > 0);
foreach $m (sort keys %MyComponents) {
my($TestLabelName) = $m . "_lastregressed";
ReadInLabelSpec("p4 label -o $TestLabelName", $m);
}
foreach $f (sort keys %FilesToSync) {
next unless ($f =~ /^\/\/[^\/]+\/\.\.\.$/);
warn "Component $FilesToSync{$f} contains view \"$f\", which is too general.\n";
$nerrs++;
}
die "$nerrs found. Exiting\n" if ($nerrs > 0);
if ($UpdateAllFiles) {
system "p4 label -d $tmplabel\n"
if (defined($LabelList{$tmplabel}));
system "p4 label -o $tmplabel | p4 label -i\n";
foreach $l (sort @LabelsToGet) {
print "Adding contents of $l to temporary label\n";
system "p4 labelsync -l $tmplabel -a \@$l > $tmpname\n";
}
foreach $l (sort keys %FilesToSync) {
print "Adding contents of $l to temporary label\n";
system "p4 labelsync -l $tmplabel -a $l > $tmpname\n";
}
print "The next command is a 'print' statement - change to 'system' to run this!\n";
print "p4 sync $minus_n \@$tmplabel\n";
} else {
foreach $l (sort keys %FilesToSync) {
print "The next command is a 'print' statement - change to 'system' to run this!\n";
print "p4 sync $minus_n $l\n";
}
}
#
# Read in the list of labels and return it as an associative array.
#
sub ReadInLabels {
my($cmd) = @_;
my(%labellist, $l);
open(CMD, "$cmd|") || die "Cannot run \"$cmd\"\n";
while (<CMD>) {
$labellist{$1}++ if (/^Label\s+(\S+)\s+/);
}
close(CMD);
return %labellist;
}
#
# Read in a label specification and remember the "View:" section, returning it
# in the global variable "%FilesToSync".
#
sub ReadInLabelSpec {
my($cmd, $componentname) = @_;
open(CMD, "$cmd|") || die "Cannot run \"$cmd\"\n";
while (<CMD>) {
$Inview++ if /^View:/;
next unless ($Inview > 0);
if (/^\s*(\/\/.*)\s*$/) {
$FilesToSync{$1} = $componentname;
}
}
close(CMD);
}