#!/usr/bin/perl package Icp4; use Cwd; use File::Basename; use strict; use warnings; use vars qw($debug $nflag %p4_fs_constant @icstudio_patterns); $debug = 0; $nflag = 0; %p4_fs_constant = ( 'UNKNOWN' => 0, 'NOT_EXIST' => 1, 'IS_A_DIR' => 2, 'NOT_IN_P4' => 3, 'NOT_OPENED' => 4, 'OPENED_HERE' => 5, 'OPENED_OTHER' => 6, ); # list of patterns of files checked into perforce @icstudio_patterns = ( '\.part_\d+$', # schematic '\.ssht_\d+$', # schematic '\.sgfx_\d+$', # schematic '\/schem_id$', # schematic '\/schem_data$', # schematic '\.smbl_\d+$', # symbol '\.iccel_\d+$', # layouts '\.iclib_\d+$', # layouts 'template\.attr$', # layout 'symbol\.attr$', # logic 'schematic\.attr$', # logic 'component\.attr$', # logic 'part\.attr$', # logic 'sheet\.attr$', # logic # '\.fz_\d+$', # freeze tags ); ################################################################################ # public functions ################################################################################ # files -- # sub files { my(@files) = (); my($file); open(P4, "p4 files ...|"); while () { ($file) = /(\/\/\S*)/; # printf("file = %s %s\n", $file, $_); # skip deleted files for now next if /- delete /; push(@files, $file); } close(P4); return(@files); } # files # opened - # results of p4 opened. # Results are returned in a hash with the file name as key and the # opened status as the value. sub opened { my($depotFile); my(@bits, @open_dir_bits); my($rev, $file, $match, $i, $j); my($dir, @dir_bits); my(%files, $status); # get current directory $dir = cwd(); @dir_bits = split(/\//, $dir); shift @dir_bits; open(P4, "p4 opened |") || die("ERROR: Cannot run 'p4 opened'.\n"); while () { chomp; # parse the depot name. @bits = split(/\s+/,$_); @open_dir_bits = split(/[\/\#]+/,$bits[0]); # get rid of extra leading '/' shift @open_dir_bits; $rev = pop @open_dir_bits; $file = pop @open_dir_bits; $match = 1; die if ($open_dir_bits[0] ne "depot"); $i = $#open_dir_bits; $j = $#dir_bits; die if ($i == -1); die if ($j <= 0); while (1) { if ($dir_bits[$j] ne $open_dir_bits[$i]) { $match = 0; last; } $j--; $i--; last if ($open_dir_bits[$i] eq "depot"); if ($j == -1) { $match = 0; last; } } if ($match) { $files{"$file"} = $status; } } close(P4); return(%files); } # opened # fstat -- # Use p4 fstat to build a hash of the files p4 status. sub fstat { my(@files) = @_; my(@p4files); my(%fstat); my($depotFile, $clientFile, $action, $change); my($headAction, $headType, $headTime, $headRev, $headChange, $haveRev); my($absFile); my($tmpfile) = "/tmp/$$.fstat"; open(FILES,">$tmpfile") or die "ERROR: cannot open $tmpfile\n"; # initialize the file status my($file); foreach $file (@files) { $absFile = Cwd::abs_path(dirname($file)) . '/' . basename($file); printf STDERR "# DEBUG: $absFile\n" if ($debug > 0); if (-d $absFile) { $fstat{$absFile} = ''; } elsif (-e $absFile) { $fstat{$absFile} = '???'; # push(@p4files, $file) if ($file !~ /\#/); print FILES "$file\n"; } else { # files doesn't exist, what should I do? } } close FILES; # my($files) = join(' ', @p4files); # don't look at stderr, or it will jumble the order of stdout in the pipe # open(STAT, "p4 fstat $files 2>/dev/null |") or die "ERROR: Couldn't run 'p4 fstat'"; # printf(STDERR "# DEBUG: p4 fstat $files 2>&1 |\n"); # Using files, because too long files can cause it to fail. # don't look at stderr, or it will jumble the order of stdout in the pipe open(STAT, "p4 -x $tmpfile fstat 2>/dev/null |") or die "ERROR: Couldn't run 'p4 fstat'"; $action = undef; while () { if (/^\.\.\. depotFile (.*)/) { $depotFile = $1; $action = undef; $change = undef; next; } if (/^\.\.\. clientFile (.*)/) { $clientFile = $1; next; } if (/^\.\.\. action (.*)/) { $action = $1; next; } if (/^\.\.\. change (.*)/) { $change = $1; next; } if (/^\.\.\. headAction (.*)/) { $headAction = $1; next; } if (/^\.\.\. headType (.*)/) { $headType = $1; next; } if (/^\.\.\. headTime (.*)/) { $headTime = $1; next; } if (/^\.\.\. headRev (.*)/) { $headRev = $1; next; } if (/^\.\.\. headChange (.*)/) { $headChange = $1; next; } if (/^\.\.\. haveRev (.*)/) { $haveRev = $1; next; } # blank line is end of each file if (/^\s*$/) { if (defined($clientFile) && defined($action)) { $fstat{$clientFile} = $action; } elsif (defined($clientFile) && defined($headAction) && $headAction eq "delete") { $fstat{$clientFile} = "delete"; } else { $fstat{$clientFile} = "#$haveRev/#$headRev"; } if ($debug > 0) { print(STDERR "# DEBUG: depot = $depotFile\n"); print(STDERR "# DEBUG: client = $clientFile\n"); print(STDERR "# DEBUG: action = $action\n"); print(STDERR "# DEBUG: change = $change\n"); print(STDERR "# DEBUG: haveRev = $haveRev\n"); print(STDERR "# DEBUG: headRev = $headRev\n"); } } } close(STAT); if ($debug > 0) { foreach $file (keys(%fstat)) { printf(STDERR "# DEBUG: $file - fstat = \"$fstat{$file}\"\n"); } } unlink $tmpfile; return(%fstat); } # fstat # where -- # returns results of p4 where sub where { my($depot, $client, $file); my(@where); open(P4, "p4 where 2>&1 |") || die "ERROR: Couldn't run 'p4 where'\n"; while () { return('error') if (/ (not|unknown) /); ($depot, $client, $file) = split(); $depot =~ s/\.\.\.//g; $client =~ s/\.\.\.//g; $file =~ s/\.\.\.//g; push(@where, [$depot, $client, $file]); } close(P4); return(@where); } # where # is_icstudio_file -- # returns true if file is an icstudio filetype sub is_icstudio_file { my($s) = @_; my($p); foreach $p (@icstudio_patterns) { if ($s =~ /$p/) { return 1; } } return 0; } # get_libpath -- # return the library path given the working dir (MGC_WD) and the library name sub get_libpath { my($workdir, $libvar, $noinfo) = @_; my($map, $mgc_location_map, $pattern); my($libpath, $ignore, $found); ($ignore, $pattern) = split('\$', $libvar); foreach $map ("mgc_location_map", "mgc_location_map.global", "mgc_location_map.local") { $mgc_location_map = "${workdir}/mgc/${map}"; if (-f $mgc_location_map) { open(MAP, "$mgc_location_map"); unless ($noinfo) { print "# INFO: Searching $mgc_location_map for \"$pattern\"\n"; } $found = 0; while () { next if (/^\s*#/); if (/^\s*\$$pattern\b/) { $found = 1; next; } if ($found) { $libpath = $_; chop $libpath; print "# INFO: Found $libpath\n" unless $noinfo; return ($libpath); } } close(MAP); } } return ("UNKNOWN"); } # get_listoflib -- # return a list of all libraries given the working dir (MGC_WD) sub get_listoflib { my($workdir) = @_; my($map, $mgc_location_map); my($libpath, $ignore, $found); my(@result) = (); my($pattern) = "-t LIBRARY"; my($libn); foreach $map ("mgc_location_map", "mgc_location_map.global", "mgc_location_map.local") { $mgc_location_map = "${workdir}/mgc/${map}"; if (-f $mgc_location_map) { open(MAP, "$mgc_location_map"); print "# INFO: Searching $mgc_location_map for \"$pattern\"\n"; $found = 0; while () { next if (/^\s*#/); if (/$pattern/i) { ($libn) = split; $libn =~ s/\$//; push @result, $libn; } } close(MAP); } } return (@result); } # p4user -- # returns p4 user name sub p4user { open(P4, "p4 info|"); my($name); while () { if (($name) = /^User name: (\S+)/) { return $name; } } return "UNKNOWN"; } # p4client -- # returns p4 client name sub p4client { open(P4, "p4 info|"); my($name); while () { if (($name) = /^Client root: (\S+)/) { return $name; } } return "UNKNOWN"; } # p4home -- # same as p4client sub p4home { return ( &p4client() ); } sub p4_stat_constant { my($name) = @_; if (defined $p4_fs_constant{$name}) { return ($p4_fs_constant{$name}); } else { return 0; } } # openStat -- # Use p4 fstat to find a file p4 status. sub openStat { my($file) = @_; my($result) = $p4_fs_constant{'UNKNOWN'}; my($depotFile, $clientFile, $action, $change); my($headAction, $headType, $headTime, $headRev, $headChange, $haveRev); my($absFile); # initialize the file status $absFile = Cwd::abs_path(dirname($file)) . '/' . basename($file); printf STDERR "# DEBUG: $absFile\n" if ($debug > 0); if (-d $absFile) { $result = $p4_fs_constant{'IS_A_DIR'}; } elsif (! -e $absFile) { # files doesn't exist, what should I do? $result = $p4_fs_constant{'NOT_EXIST'}; } # don't look at stderr, or it will jumble the order of stdout in the pipe open(STAT, "p4 fstat $file 2>/dev/null |") || die "ERROR: Couldn't run 'p4 fstat'"; # printf(STDERR "# DEBUG: p4 fstat $file 2>&1 |\n"); while () { $result = $p4_fs_constant{'NOT_OPENED'} if /... depotFile/i; $result = $p4_fs_constant{'OPENED_HERE'} if /... action edit/i; $result = $p4_fs_constant{'OPENED_OTHER'} if /... otherAction[0-9]+ edit/i; } close(STAT); return($result); } # openStat # outstanding_change -- # returns list of outstanding changes in current client space sub outstanding_changes { my($ok, $user, $client, $changes); $user = p4user(); $client = p4client(); print "# INFO: Looking for pending changes (\"p4 changes -s pending -u $user\")\n" if ($debug > 0); open (P4, "p4 changes -s pending -u $user | grep $client |"); $changes = 0; while () { if ($changes == 0) { $changes = 1; print "# ERROR: Please take care of these pending changes first:\n"; print "# HINT: Run \"p4 change \" to see what the outstanding changes are.\n"; print "# HINT: Use the \"-i\" flag to ignore outstanding changes *only if* you know they are not yours.\n"; } print; } return ($changes); } sub submit_open_files { my($cells, $flags) = @_; my($file, $opened, $editor); # create a changelist by combining list of opened # files with changelist template my($template) = ".template.$$"; my($openfiles) = ".opened.$$"; my($changelist) = ".changelist.$$"; system("p4 change -o > $template"); system("p4 opened $cells | sort | uniq > $openfiles"); open (TEMPLATE, "$template") or die "ERROR: cannot open $template."; open (OPENFILES, "$openfiles") or die "ERROR: cannot open $openfiles."; open (CHANGELIST, ">$changelist") or die "ERROR: cannot open $changelist."; $opened = 0; while (