#!/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 () { $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 () { $Inview++ if /^View:/; next unless ($Inview > 0); if (/^\s*(\/\/.*)\s*$/) { $FilesToSync{$1} = $componentname; } } close(CMD); }