#!/usr/bin/perl
# $Id: //depot/cad/tools/1.0/icp4-1.1/bin/p4ci#31 $
#
# CSWITCH CORPORATION CONFIDENTIAL PROPRIETARY
#
# This file contains information which is the
# proprietary property of Cswitch Corporation.
# This file is confidential and its contents may
# not be disclosed without the expressed written
# consent of Cswitch Corporation.
# For each cell given:
# error check
# make list of cells to add/submit
# bring up edit page
# Do together:
# revert -a
# add
# submit
use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin/../lib";
use Icp4;
use Getopt::Std;
use File::Basename;
use Cwd;
our ($Program, %opt, $islayout, $nflag, $rflag, $client, $cells, $support);
our $attr_sfx = ".Ic_cell_template.attr";
our $cell_sfx = ".iccel_[0-9]+";
our $keep = 1;
# each file in raw_list has a file status:
# raw
# edit
# add
# revert
our %file_status;
our @raw_list;
our @revert_list;
our @add_list;
our @edit_list;
our @user_add_list;
our @user_edit_list;
# turn on logging of p4ci to ~/p4ci.log
my $P4CI_LOG = 1;
#my $P4CI_LOG = 0;
MAIN: {
$Program = $FindBin::Script;
getopts('hDnirk:', \%opt);
if ($opt{h} || $#ARGV < 0) {
print "Usage: $Program [-h] [-n] [-i] [-k num] [-r] cell+\n";
print " -h: help usage\n";
print " -n: show operation only\n";
print " -i: ignore pending changes (dangerous!)\n";
print " -k: number of versions to keep\n";
print " (default: $keep, works for layout only for now)\n";
print " -r: remain open for edit after checkin\n";
print " (default: locked after checkin)\n";
print " cell: cell name(s)\n";
print " (hint: use \\\$ for cells with \$ character in their name)\n";
exit 0;
}
$nflag = ($opt{n}) ? "-n" : "" ;
$rflag = ($opt{r}) ? "-r" : "" ;
$keep = $opt{k} if ($opt{k});
#$Icp4::debug = ($opt{D}) ? 1 : 0 ;
# keep Icp4 quiet
$Icp4::debug = 0;
$Icp4::nflag = ($opt{n}) ? 1 : 0 ;
# make sure p4 is configured
if (&Icp4::where() eq 'error') {
print "ERROR: Current directory not in client. Please check P4 variables.\n";
exit -1;
}
$client = &Icp4::p4client();
$support = "ryu\@cswitch.com";
# check for outstanding changes
if (!$opt{i} && &Icp4::outstanding_changes()) {
exit -1;
}
if ($islayout = &Icp4::is_layout_view()) {
# At this point, the raw_list contains only attr and iccel files
# that are known to exist in unix.
get_raw_list();
if ($opt{D}) {
&Icp4::list_debug_print("raw_list", @raw_list);
}
# revert list is a subset of raw_list and are files that have not been modified
get_revert_list();
if ($opt{D}) {
&Icp4::list_debug_print("revert_list", @revert_list);
}
# edit list is a subset of raw_list and are files that have been modified
get_edit_list();
if ($opt{D}) {
&Icp4::list_debug_print("edit_list", @edit_list);
}
# add list is a subset of raw_list and new files to p4
get_add_list();
if ($opt{D}) {
&Icp4::list_debug_print("add_list", @add_list);
}
# Bring up the form. all files listed will be
# reverted if not changed, added if it's new, and then submitted.
p4_add_submit_revert();
} else {
old_p4ci();
}
exit 0;
}
sub get_raw_list {
my ($cell, $orig_cell, $path);
my ($attr_file, $orig_attr_file, $ic_pat, $ic_file);
my ($openStat, $expectedStat);
my (@keep_list, $i, $s, $orig_cnt, $exists);
$cells = "";
foreach $cell (@ARGV) {
# get rid of any trailing "/" and convert to full path
$cell =~ s/\/$//;
$path = Cwd::abs_path(dirname($cell));
$orig_cell = $cell;
# escape the name for p4
if ($cell =~ /\$/) {
$cell =~ s/\$/\\\$/g;
if ($opt{D}) {
print "# DEBUG: path = $path\n";
print "# DEBUG: cell = $cell\n";
print "# DEBUG: orig_cell = $orig_cell\n";
}
}
if (-d $orig_cell) {
# remove any .iclck files
unlink <${orig_cell}/.*.iclck>;
$cells = $cells . "${cell}/... ";
#$cell_attr = $cell . $attr_sfx;
$ic_pat = $cell . $cell_sfx;
$orig_attr_file = "${orig_cell}/${orig_cell}${attr_sfx}";
$attr_file = "${cell}/${cell}${attr_sfx}";
if (has_lckfiles($cell)) {
print "WARNING: Cell \"$cell\" is locked by icstudio. Skipping cell \"$cell\".\n";
next;
}
# workaround for some attribute files that have .s
if (! -f "$orig_attr_file") {
print "WARNING: $orig_attr_file not found. Trying the .s trick.\n";
$orig_attr_file = "${orig_cell}/${orig_cell}.s${attr_sfx}";
$attr_file = "${cell}/${cell}.s${attr_sfx}";
if (! -f "$orig_attr_file") {
print "WARNING: $orig_attr_file not found either. Skipping \"$orig_cell\".\n";
next;
} else {
print "INFO: Found $orig_attr_file.\n";
}
}
&Icp4::get_keep_list($orig_attr_file, \@keep_list);
if ($opt{D}) {
&Icp4::list_debug_print("keep_list", @keep_list);
}
$orig_cnt = $#keep_list;
for ($i=0; $i <= ($orig_cnt - $keep); $i++) {
shift @keep_list;
}
if ($opt{D}) {
print "# DEBUG: After trimming ... $#keep_list\n";
&Icp4::list_debug_print("keep_list", @keep_list);
}
# existence check for @keep_list files
$exists = 1;
foreach $i (@keep_list) {
$ic_file = "$path/$orig_cell/$i";
if (! -f $ic_file) {
$exists = 0;
print "ERROR: \"$ic_file\" not found. Skipping cell \"$cell\".\n";
last;
}
}
if ($exists) {
$s = "${path}/${orig_attr_file}";
push (@raw_list, $s);
$file_status{$s} = "raw";
foreach $i (@keep_list) {
$s = "${path}/${orig_cell}/$i";
push (@raw_list, $s);
$file_status{$s} = "raw";
}
}
}
}
close FOUT;
}
sub get_revert_list {
my($rawfile) = ".rawlist.$$";
my ($s);
open (FOUT, ">$rawfile") or die "ERROR: cannot open $rawfile.";
foreach $s (@raw_list) {
print FOUT "$s\n";
}
close FOUT;
open (REVERT, "p4 -x $rawfile revert -a -n 2>&1 |") or die "ERROR: cannot run p4 revert.";
while (<REVERT>) {
if (($s) = /(.*)#\d+ - was edit, reverted/) {
# change name
$s =~ s#//depot#$client#;
push (@revert_list, $s);
if ($file_status{$s}) {
$file_status{$s} = "revert";
} else {
die "INTERNAL ERROR: file status of $s not found. Please notify $support.\n";
}
}
}
close REVERT;
unlink $rawfile unless ($opt{D});
}
sub get_edit_list {
my (%p4files) = Icp4::fstat(@raw_list);
my ($s);
@edit_list = ();
foreach $s (sort keys %p4files) {
# print "# DEBUG: file \"$s\" has p4 status \"$p4files{$s}\".\n" if ($opt{D});
if ($p4files{$s} eq 'edit') {
if ($file_status{$s}) {
if ($file_status{$s} ne 'revert') {
push (@edit_list, $s);
$file_status{$s} = "edit";
}
} else {
die "INTERNAL ERROR: file status of \"$s\" not found during get_edit_list. Please notify $support.\n";
}
}
}
}
sub get_add_list {
my (%p4files) = Icp4::fstat(@raw_list);
my ($s);
@add_list = ();
foreach $s (sort keys %p4files) {
if (($p4files{$s} eq '???') || ($p4files{$s} eq 'delete')) {
if ($file_status{$s}) {
push (@add_list, $s);
$file_status{$s} = "add";
} else {
die "INTERNAL ERROR: file status of \"$s\" not found during get_add_list. Please notify $support.\n";
}
}
}
}
sub p4_add_submit_revert {
my($file, $editor);
my($proceed);
my($extract);
my($list_ptr, $s, $type, $depot_file);
# create a changelist by combining list of opened
# files with changelist template
my($template) = ".template.$$";
my($changelist) = ".changelist.$$";
system("p4 change -o > $template");
# When a workspace has nothing opened, the $template file will have no "Files:" line
# Add it to the bottom of the file David Guan
my($cnt);
chop($cnt = `egrep -c Files $template`);
if ($cnt == 1) {
system("echo 'Files:' >> $template");
}
open (TEMPLATE, "$template") or die "ERROR: cannot open $template.";
open (CHANGELIST, ">$changelist") or die "ERROR: cannot open $changelist.";
while (<TEMPLATE>) {
if (/^Files:/) {
print CHANGELIST;
foreach $file (sort keys %file_status) {
$depot_file = $file;
$depot_file =~ s#$client#//depot#;
print CHANGELIST "\t$depot_file\t# add\n" if ($file_status{$file} eq "add");
}
foreach $file (sort keys %file_status) {
$depot_file = $file;
$depot_file =~ s#$client#//depot#;
print CHANGELIST "\t$depot_file\t# edit\n" if ($file_status{$file} eq "edit");
}
last;
} else {
print CHANGELIST;
}
}
close CHANGELIST;
$editor = $ENV{'P4EDITOR'} ? $ENV{'P4EDITOR'} :
$ENV{'EDITOR'} ? $ENV{'EDITOR'} : "vim" ;
# now bring up the editor and have user say something nice
system ("$editor $changelist");
# Read the editted changelist, and look for the "<enter description here>".
# If found, then do no p4 action, otherwise, create the real p4 changelist.
open (CHANGELIST, "$changelist") or die "ERROR: cannot open $changelist.";
$proceed = 1;
$extract = 0;
while (<CHANGELIST>) {
if (/<enter description here>/) {
$proceed = 0;
next;
}
if (/^Files:/) {
if ($proceed == 0) {
print "ERROR: Change description missing. You must enter a description to continue.\n";
exit -1;
}
$extract = 1;
next;
}
if ($extract) {
if (($s, $type) = /^\t(.*)\t# (add|edit)/) {
$s =~ s#//depot#$client#;
if ($type eq "add") {
push (@user_add_list, $s);
} else {
push (@user_edit_list, $s);
}
# check the file are still under the same list
if ($file_status{$s} ne $type) {
print "ERROR: file \"$s\" should have been \"$file_status{$s}\" but was set to \"$type\".\n";
exit -1;
}
next;
}
}
}
close CHANGELIST;
if ($opt{D}) {
&Icp4::list_debug_print("user_add_list", @user_add_list);
&Icp4::list_debug_print("user_edit_list", @user_edit_list);
}
# now do these together
p4_add(\@user_add_list) if ($#user_add_list >= 0);
p4_submit($changelist) if (($#user_add_list >= 0) || ($#user_edit_list >= 0));
p4_revert_cells() if (!$opt{r});
# cleanup
unlink $template unless ($opt{D});
unlink $changelist unless ($opt{D});
}
sub p4_add {
my($list_ptr) = @_;
my($s, $cmd);
my($template) = ".addfiles.$$";
open (TEMPLATE, ">$template") or die "ERROR: cannot open $template.";
foreach $s (@{$list_ptr}) {
print TEMPLATE "$s\n";
}
close TEMPLATE;
$cmd = "p4 -x $template add $nflag";
if ($opt{n}) {
print "INFO: Adding (not run) new files using (\"$cmd\")\n";
} else {
print "INFO: Adding new files using (\"$cmd\")\n";
}
system $cmd;
unlink $template unless ($opt{D});
}
sub p4_submit {
my ($changelist) = @_;
my ($s, $cmd);
$cmd = "p4 submit $rflag -i < $changelist";
if ($opt{n}) {
print "INFO: Submitting (not run) changes using (\"$cmd\")\n";
} else {
print "INFO: Submitting changes using (\"$cmd\")\n";
}
system $cmd unless ($opt{n});
}
# Unused: this may leave some files in edit mode because p4co did 'p4 edit cell/...'
# Better to use p4_revert_cells instead
sub p4_revert {
my($list_ptr) = @_;
my($s, $cmd);
my($template) = ".revertfiles.$$";
open (TEMPLATE, ">$template") or die "ERROR: cannot open $template.";
foreach $s (@{$list_ptr}) {
print TEMPLATE "$s\n";
}
close TEMPLATE;
$cmd = "p4 -x $template revert -a $nflag";
if ($opt{n}) {
print "INFO: Reverting (not run) unchanged files using (\"$cmd\")\n";
} else {
print "INFO: Reverting unchanged files using (\"$cmd\")\n";
}
system $cmd;
unlink $template unless ($opt{D});
}
sub p4_revert_cells {
my($cmd);
$cmd = "p4 revert -a $nflag $cells > /dev/null 2>&1";
if ($opt{n}) {
print "INFO: Reverting (not run) all unchanged files using (\"$cmd\")\n";
} else {
print "INFO: Reverting all unchanged files using (\"$cmd\")\n";
}
system $cmd;
}
# has_lckfiles --
# returns true if lck files found
sub has_lckfiles {
my($dir) = @_;
open(FIND, "find $dir -name \*.lck\* -print |") or die "ERROR: cannot run find.";
while (<FIND>) {
return 1;
}
return 0;
}
# ----------------------------------------------------------------------------------------
# TODO: support schematics
# old_p4ci --
# old p4ci, which is used for schematics
sub old_p4ci {
my ($p4local_flag, $cell, $cell_dirname);
if ($P4CI_LOG) {
$p4local_flag = "-L $ENV{HOME}/p4ci.log";
}
$cells = "";
# Let's check for lck files first. And if found any, just quit
foreach $cell (@ARGV) {
$cell_dirname = $cell;
if ($cell =~ /\$/) {
$cell =~ s/\$/\\\$/g;
}
if (-d $cell_dirname) {
if (has_lckfiles($cell)) {
print "ERROR: Cell \"$cell\" is locked by icstudio. Please remove lock and try again.\n";
exit -1;
}
}
}
foreach $cell (@ARGV) {
$cell_dirname = $cell;
if ($cell =~ /\$/) {
$cell =~ s/\$/\\\$/g;
}
if (-d $cell_dirname) {
# remove any .iclck files
unlink <${cell_dirname}/.*.iclck>;
# add any new $cell.* files ...
system "p4local $p4local_flag -a ${cell}.*";
# or files under the $cell directory
if ((system "p4local $p4local_flag -a $cell") == 0) {
$cells = $cells . " ${cell}.* ${cell}/...";
} else {
# some bad happened, maybe a lck file found
print "INFO: No operation performed for cell $cell.\n";
}
}
}
if ($cells ne "") {
if (!$opt{r}) {
print "INFO: Reverting any unmodified files (\"p4 revert $nflag -a $cells\")\n";
system "p4 revert $nflag -a $cells";
}
&Icp4::submit_open_files($cells, $rflag);
}
exit 0;
}