#!/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; }