# This module contains all code common to different phases of the # VSS to Perforce converter. require 5.0; package convert; use strict; use vars qw(@ISA @EXPORT); use integer; use Carp; use Cwd; require Exporter; @ISA = qw(Exporter); @EXPORT = qw( $metadata_dir $data_dir $root $client_root $time_interval $lowercase_pathnames $lowercase_filenames $lowercase_usernames $lowercase_extensions $typemap_regexp $perform_verify $skip_ss_get_errors $bypass_metadata $debug_level run emkdir p4dir_and_file ); # set values of global variables $convert::metadata_dir = "metadata"; $convert::data_dir = "data"; # read the configuration file and set up needed variables my %option = read_form("config"); die "must specify root" unless exists($option{'root'}); $convert::root = forward_slash($option{'root'}); $convert::depot = (exists($option{'depot'})) ? $option{'depot'} : "depot"; $convert::label_prefix = (exists($option{'label_prefix'})) ? $option{'label_prefix'} : ""; # The first line of the depot $convert::depot_root = (exists($option{'depot_root'})) ? $option{'depot_root'} : "main"; $convert::client_root = forward_slash(cwd() . "/" . $convert::data_dir); $convert::time_interval = (exists($option{'time_interval'})) ? $option{'time_interval'} : 600; $convert::lowercase_pathnames= (exists($option{'lowercase_pathnames'})) ? ($option{'lowercase_pathnames'} =~ /y/i) : 0; $convert::typemap_regexp= (exists($option{'typemap_regexp'})) ? $option{'typemap_regexp'} : ""; $convert::lowercase_filenames= (exists($option{'lowercase_filenames'})) ? ($option{'lowercase_filenames'} =~ /y/i) : 0; $convert::lowercase_extensions= (exists($option{'lowercase_extensions'})) ? ($option{'lowercase_extensions'} =~ /y/i) : 0; $convert::lowercase_usernames= (exists($option{'lowercase_usernames'})) ? ($option{'lowercase_usernames'} =~ /y/i) : 0; $convert::ss_options = (exists($option{'ss_options'})) ? $option{'ss_options'} : ""; $convert::debug_level = (exists($option{'debug_level'})) ? $option{'debug_level'} : 0; $convert::bypass_metadata= (exists($option{'bypass_metadata'})) ? ($option{'bypass_metadata'} =~ /y/i) : 0; $convert::perform_verify= (exists($option{'perform_verify'})) ? ($option{'perform_verify'} =~ /y/i) : 0; $convert::skip_ss_get_errors= (exists($option{'skip_ss_get_errors'})) ? ($option{'skip_ss_get_errors'} =~ /y/i) : 0; # Run a command, optionally piping a string into it on stdin. # Returns whatever the command printed to stdout. The whole thing is # optionally logged. NOTE that stderr is not redirected. sub run { my ($syscall,$stuff_to_pipe_in) = @_; my $result; if(defined($stuff_to_pipe_in)) { # Use a temporary file because not all systems implement pipes open(TEMPFILE,">pipeto") or die "can't open pipeto: $!\n"; print TEMPFILE $stuff_to_pipe_in; close(TEMPFILE); $result = `$syscall <pipeto`; unlink("pipeto"); } else { $result = `$syscall`; } if($convert::debug_level > 0) { # append to a file - that way if the converter dies the file will # be up to date, and this mechanism doesn't rely on an open filehandle open(LOGFILE,">>logfile") or die "can't open logfile: $!"; print LOGFILE "\n\nCommand: $syscall\n"; print LOGFILE $result; close(LOGFILE); } return $result; } # Ensure a directory exists - make it and all required parents if it doesn't. # Die on failure. # Accepts a list of directories, makes them all with perms 0755 sub emkdir { my ($current,$prev,$dir); for (@_) { next if(length($_) < 2); # skip over drive letters and UNCs to avoid calling (eg) # mkdir a: or mkdir //machinename $prev= (/^[a-zA-Z]:/) ? 2 : ( (m@//@) ? index($_,'/',2) : 0); # mkdir for each subpath as necessary do { $current=index($_,'/',$prev+1); $dir= ($current == -1) ? $_ : substr($_,0,$current); if(! -d $dir) { mkdir($dir,0755) or croak "can't mkdir $dir: $!"; } $prev=$current; } while( $current != -1); } } sub read_form # read a Perforce style form { my $file = shift; my (%hash,$current_keyword); open(F,"<$file") or croak("can't open $file: $!"); while(<F>) { s/\s*#.*$//; # kill comments and any whitespace preceding the comment if(/^$/) { # empty line or line with just a comment undef($current_keyword); } elsif(substr($_,0,1) eq "\t") { croak("unrecognized line") if(!defined($current_keyword)); s/^\t//; $hash{$current_keyword} .= $_; } elsif(/(.*?):\s*(.*)/) { # keyword is everything up to the *first* colon $hash{$current_keyword=$1} = $2; } } close(F); return %hash; } sub forward_slash { my $s = shift; $s =~ s@\\@/@g; return $s; } # p4dir_and_file computes the directory and filename relative to the client root # given the VSS filename. sub p4dir_and_file { my $client_rel_dir = shift; $client_rel_dir =~ s%[\000-\031@#]%_%g; # convert unprintable, @, # to _ $client_rel_dir =~ s@^\$@$convert::depot_root@; $client_rel_dir =~ s@/([^/]*)$@@; # strip off filename my $client_file = $1; $client_rel_dir = lc($client_rel_dir) if($convert::lowercase_pathnames); $client_file = lc($client_file ) if($convert::lowercase_filenames); if($convert::lowercase_extensions) { $client_file =~ /(.*?)(\.[^.]+)?$/; my ($cl_base,$cl_ext) = ($1,$2); $cl_ext = lc($cl_ext); $client_file = $cl_base . $cl_ext; } return ($client_rel_dir,$client_file); } # return a string joining the pathname components, # ensuring there is one / between components and there is no trailing slash sub join_paths { my $path=""; for (@_) { if(defined($_) && $_ ne "") { $path .= (substr($_,-1) eq '/') ? $_ : $_ . '/'; } } chop($path) if(substr($path,-1) eq '/'); return $path; } # get the given file from VSS (specified as a complete "project" path) # and name it according to its name in Perforce (given client dir and filename) sub get_vss_file { my ($vss_file,$revision,$client_dir,$client_file) = @_; emkdir($client_dir); my $client_filepath = join_paths($client_dir, $client_file); unlink($client_filepath); $revision = "-v$revision" if $revision; # if $revision not set, don't give -v flag (tempobj hack) convert::run("ss get \"$vss_file\" -W -GL\"${client_dir}\" $revision $convert::ss_options"); # get it writable so that we can unlink it later $vss_file =~ m@/([^/]*)$@; my $vss_filepath = join_paths($client_dir, $1); rename($vss_filepath,$client_filepath) unless($vss_filepath eq $client_filepath); } 1;
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#2 | 1513 | Peter Steiner |
- writing to the logfile in verify.pl didn't work - convert.pm: get_vss_file failed sometimes (when there was a file in the working directory with the same name as one in the VSS archive) - the timestamp of a label is now set from the VSS label timestamp - files can be added using perforce typemaps for determining the file type (though this is disabled because it is likely to break verify.pl when the revisions are compared using different keyword expansion methods...). See convert::typemap_regexp - new option lowercase_extensions (similar to lowercase_filenames) - option convert::perform_verify (no more need to press a key during nightly conversion tests) - label name transformations to cope better with special character restrictions. You probably want to adjust for your own purposes; see readhist.pl line 169ff. - output of convert.pl adorned with timestamps - changes for Swiss time format are prepared (in the comments of readhist.pl), though this probably should be done with specifying a regexp in the config file - various fixes when using german umlauts, especially in Windows login names propagated to VSS, in labels and comments |
||
#1 | 1510 | Peter Steiner | integrated from Roberts vsstop4 branch | ||
//guest/robert_cowham/perforce/utils/vsstop4/convert.pm | |||||
#1 | 237 | Robert Cowham |
Improved version of vsstop4. Makes life easier if importing into a depot which already contains stuff you want to keep. Also handles other people updating the depot at the same time. See changes labelled RHGC, and new items in config file. |