- #!/usr/local/bin/perl -w
- =head1 NAME
- vcp - Copy versions of files between repositories and/or RevML
- =head1 SYNOPSIS
- # interactive mode:
- vcp [vcp_opts]
- # scriptable command line mode:
- vcp [vcp_opts] <source> <dest>
- # getting options from a file:
- vcp vcp:config.vcp
- # help output:
- vcp help [topic]
- # documentation generation:
- vcp html <destination dir>
- =head1 DESCRIPTION
- C<vcp> ('version copy') copies versions of files from one repository to another,
- translating as much metadata as possible along the way. This allows you to
- copy and translate files and their histories between revision storage systems.
- Supported source and destination types are C<cvs:>, C<p4:>, and C<revml:>.
- =head2 Copying Versions
- The general syntax of the vcp command line is:
- vcp [<vcp options>] <source> <dest>
- The three portions of the command line are:
- =over
- =item C<E<lt>vcp optionsE<gt>>
- Command line options that control the operation of the C<vcp> command, like
- C<-d> for debugging or C<-h> for help. There are very few global options,
- these are covered below. Note that they must come before the
- C<E<lt>sourceE<gt>> specification.
- =item C<E<lt>sourceE<gt>>
- Were to extract versions from, including any command line options needed to
- control what is extracted and how. See the next section.
- =item C<E<lt>destE<gt>>
- Where to insert versions, including any command line options needed to control
- how files are stored. See the next section.
- =back
- =head2 Specifying Repositories
- The C<E<lt>sourceE<gt>> and C<E<lt>destE<gt>> specifications specify a
- repository and provide any options needed for accessing that repository.
- These spefications may be a simple filename for reading or writing RevML
- files (if the requisite XML handling modules are installed). or a full
- repository specification like C<cvs:/home/cvs/root:module> or
- C<p4:user:password@server:port://depot/dir>.
- When using the long form to access a repository, C<E<lt>sourceE<gt>> and
- C<E<lt>destE<gt>> specification have several fields delimited by C<:>
- and C<@>, and may have trailing command line options. The full (rarely
- used) syntax is:
- scheme:user(view):password@repository:filespec [<options>]
- where
- =over
- =item C<scheme:>
- The repository type (C<p4:>, C<cvs:>, C<revml:>).
- =item C<user>, C<view>, and C<password>
- Optional values for authenticating with the repository and identifying which
- view to use. C<cvs> does not use C<view>. For C<p4>, C<view> is the client
- setting (equibalent to setting C<P4CLIENT> or using C<p4>'s C<-c> option).
- =item C<repository>
- The repository spec, CVSROOT for CVS or P4PORT for p4.
- =item C<filespec>
- Which versions of what files to move. As much as possible, this spec is
- similar to the native filespecs used by the repository indicated by the scheme.
- =item C<E<lt>optionsE<gt>>
- Command line options that usually mimic the options provided by the underlying
- repositories' command line tools (C<cvs>, C<p4>, etc).
- =back
- Most of these fields are omitted in practice, only the C<scheme> field is
- required, though (in most cases) the C<repository> field is also needed unless
- you set the appropriate environment variables (C<CVSROOT>, C<P4PORT>, etc).
- The a bit confusing, here are some examples specs:
- cvs:server:/foo
- p4:user@server://depot/foo/...
- p4:user:password@public.perforce.com:1666://depot/foo/...
- Options and formats for of individual schemes can be found in the relevant
- help topics, for instance:
- vcp help source::cvs
- See C<vcp help> for a list of source and destination topics.
- When reading and writing RevML files, a simple filename will do
- (although the long form may also be used). The special value "-" means
- to read/write stdin and stdout when used as a source or destination
- name, respectively. "-" is assumed if a specification is not provided,
- so these invocations all accomplish the same thing, reading and writing
- RevML:
- vcp
- vcp -
- vcp revml:-
- vcp revml:
- vcp - -
- vcp - revml:-
- vcp - revml:
- vcp revml:- revml:-
- vcp revml: revml:
- =head2 C<vcp> Options
- All general options to vcp must precede the C<E<lt>sourceE<gt>>.
- Scheme-specific options must be placed immediately after the
- C<E<lt>sourceE<gt>> or C<E<lt>destE<gt>> spec and before the next one.
- =over
- =item --debug, -d
- Enables logging of debugging information.
- =item --help, -h, -?
- These are all equivalent to C<vcp help>.
- =item --output-config-file=$filename
- Write the settings (parsed from the UI, the command line, or a config
- file to a file. Useful for capturing settings or user interface output.
- Does not affect running. Use "-" to emit to STDOUT.
- B<NOTE 1>: This does I<not> emit an "Options:" section containing global
- options (those listed here). Almost all of these options are not useful
- to emit; we can add an option to force their emission if need be.
- B<NOTE 2>: When using the interactive user interface, this option takes
- effect after the last interactive portion and, if vcp goes on to run a
- conversion, before any conversion is run. This occurs in addition to
- any configuration files the user may ask the interactive interface to
- write. This may change in the future (for instance, if the interactive
- dialog includes an option to extract and analyze metadata).
- =item --dont-convert
- Do not run a conversion. Useful when you just want to emit a .vcp file.
- =item --terse, -t
- Suppress verbose explanations when running the interactive UI. Has
- no effect on operation if all settings are read from the command line
- or a .vcp file.
- =back
- =head2 Getting help
- (See also L<Generating HTML Documentation|/Generating HTML Documentation>,
- below).
- There is a slightly different command line format for requesting help:
- vcp help [<topic>]
- where C<E<lt>topicE<gt>> is the optional name of a topic. C<vcp help> without
- a C<E<lt>>topicC<E<gt>> prints out a list of topics, and C<vcp help vcp>
- emits this page.
- All help documents are also available as Unix C<man> pages and using the
- C<perldoc> command, although the names are slightly different:
- with vcp via perldoc
- ================ ===========
- vcp help vcp perldoc vcp
- vcp help source::cvs perldoc VCP::Source::cvs
- vcp help source::cvs perldoc VCP::Dest::p4
- C<vcp help> is case insensitive, C<perldoc> and C<man> may or may not be
- depending on your filesystem. The C<man> commands look just like the example
- C<perldoc> commands except for the command name. Both have the advantage that
- they use your system's configured pager if possible.
- =head2 Environment Variables
- The environment is often used to set context for the source and
- destination by way of variables like P4USER, P4CLIENT, CVSROOT, etc.
- =over
- =item VCPDEBUG
- The VCPDEBUG variable acts just like C<-d=$VCPDEBUG> was present on the
- command line:
- VCPDEBUG=1
- (see L<"--debug, -d"> for more info). This is useful when VCP is
- embedded in another application, like a makefile or a test suite.
- =back
- =head2 Generating HTML Documentation
- All of the help pages in C<vcp> can be built in to an HTML tree with the
- command:
- vcp html <dest_dir>
- The index file will be C<E<lt>dest_dirE<gt>/index.html>.
- =for test_scripts t/10vcp.t t/50revml.t
- =for comment
- t/50revml.t is used too so that we actually make sure that filenames get
- passed through to the source (at least) properly.
- =cut
- use strict ;
- BEGIN {
- $ENV{VCPDEBUG} ||= grep /\A(-d|--debug)\z/, @ARGV;
- $ENV{VCPPROFILE} ||= grep /\A(--profile)\z/, @ARGV;
- if ( grep /\A(--diestack)\z/, @ARGV ) {
- $SIG{__DIE__} = sub {
- local $SIG{__DIE__} = 'DEFAULT';
- require Carp;
- Carp::confess( @_ );
- };
- @ARGV = grep ! /\A(--diestack)\z/, @ARGV;
- }
- }
- use VCP::Logger qw( lg pr BUG );
- use VCP::Debug qw( :debug ) ;
- use VCP::Utils qw( empty shell_quote program_name );
- use Getopt::Long ;
- use VCP ;
- my $program_name = program_name;
- my $terse_ui_prompts; ## set from command line
- my $output_config_file; ## Set from a command line.
- my $dont_convert;
- eval {
- my $dtd_spec ;
- my $arg;
- lg shell_quote( $program_name, @ARGV );
- my @plugins;
- ## Parse up to the first non-option, then let sources & dests parse
- ## from there.
- Getopt::Long::Configure( qw( no_auto_abbrev no_bundling no_permute ) ) ;
- parse_cli_options( \@ARGV );
- my $interactive_ui = !@ARGV; ## will also be set later if 'edit' arg
- my $vcp_filename; ## Set if we're to edit an existing file
- if ( !$interactive_ui ) {
- ## command line mode
- $arg = $ARGV[0];
- build_html_tree_and_exit( $program_name, @ARGV[1..$#ARGV] )
- if $arg eq "html";
- help_and_exit( @ARGV[1..$#ARGV] )
- if $arg eq 'help' ;
- my $vcp_spec;
- if ( $arg eq "scan" || $arg eq "filter" || $arg eq "transfer" ) {
- shift;
- }
- my @errors;
- if ( $arg eq "edit" ) {
- shift;
- $interactive_ui = 1;
- my $spec = shift;
- if ( empty $spec ) {
- push @errors, "edit requires a vcp configuration file name"
- }
- else {
- require VCP::ConfigFileUtils;
- ( $vcp_filename = $spec ) =~ s/^vcp://i;
- $vcp_spec = VCP::ConfigFileUtils::parse_config_file(
- $vcp_filename
- );
- }
- }
- else {
- ## See if it's a config file: look for vcp: or .vcp or even
- ## try sniffing it to see if it looks like one
- my $source_spec = $ARGV[0];
- require VCP::ConfigFileUtils;
- my $is_vcp_file =
- $source_spec =~ s/^vcp://i || $source_spec =~ /\.vcp\z/i;
- my $cant_be_vcp_file = ! $is_vcp_file && (
- $source_spec eq "-" ## must be a revml file
- || $source_spec =~ /\.revml\z/i
- || $source_spec =~ /^\w{2,}:/ # ignore foo:, sniff C:
- || ! -e $source_spec
- || -S _ < 1_000_000 # That would be toooo big
- );
- $vcp_spec = $is_vcp_file
- ? VCP::ConfigFileUtils::parse_config_file( $source_spec )
- : ! $cant_be_vcp_file
- ? VCP::ConfigFileUtils::parse_config_file(
- $source_spec,
- "may not be a config file"
- ) # sniff the file, return FALSE if !vcp file
- : 0;
- if ( $vcp_spec ) {
- $vcp_filename = $source_spec;
- shift @ARGV;
- }
- };
- if ( $vcp_spec ) {
- ## It's a .vcp file parsed in to @$vcp_spec.
- if ( @$vcp_spec && $vcp_spec->[0] eq "options" ) {
- shift @$vcp_spec;
- parse_cli_options( shift @$vcp_spec );
- }
- push @errors, "$vcp_filename does not specify a Source\n"
- unless $arg eq "filter"
- || ( $vcp_spec && @$vcp_spec && $vcp_spec->[0] eq "source" );
- push @errors, "$vcp_filename does not specify a Destination\n"
- unless $arg eq "scan" || $arg eq "filter"
- || ( $vcp_spec && @$vcp_spec && $vcp_spec->[-2] eq "dest" );
- push @errors,
- "command line parameters not allowed when using config file: "
- . join( " ", @ARGV )
- . "\n"
- if @ARGV;
- while ( @$vcp_spec ) {
- my ( $tag, $value ) = ( shift @$vcp_spec, shift @$vcp_spec );
- my $default_scheme;
- my $type;
- my $spec;
- my $parms;
- # Unlike the command line, we know there *must* be a source
- # and a dest in @$vcp_spec. Anything in between is filters.
- if ( $tag eq "source" || $tag eq "dest" ) {
- $default_scheme = "revml";
- $type = $tag;
- $spec = shift @$value;
- }
- else {
- $default_scheme = $tag;
- $type = "Filter";
- $spec = "";
- }
- push @plugins, load_module( $spec, $type, $default_scheme, $value );
- die "extra parameters for $tag: ", shell_quote( @$value ), "\n"
- if @$value;
- }
- }
- else {
- ## Parse the command line.
- my $type = "Source";
- while ( @ARGV ) {
- my $spec = shift;
- my $default_scheme;
- if ( $type eq "Source" ) {
- $default_scheme = "revml";
- }
- elsif ( $type ne "Dest" && find_filter( $spec ) ) {
- ## It's a filter.
- $type = "Filter";
- }
- else {
- $type = "Dest";
- $default_scheme = "revml";
- }
- ## We pass \@ARGV to the constructors for source and dest so
- ## that they may parse some of @ARGV and leave the rest.
- ## Actually, that's only important for sources, since the
- ## dests should consume it all anyway. But, for
- ## consistency's sake, I do the same to both.
- push @plugins, load_module( $spec, $type, $default_scheme, \@ARGV );
- $type = "";
- ## Fake up a destination if none was passed.
- push( @ARGV, "revml:-" ), $type = "Dest"
- if ! @ARGV && ! $plugins[-1]->isa( "VCP::Dest" ) ;
- }
- push @errors, "extra parameters: " . join( ' ', @ARGV ) . "\n"
- if @ARGV;
- }
- if ( @errors ) {
- my $errors = join( '', @errors ) ;
- $errors =~ s/^/$program_name ERROR: /mg ;
- chomp $errors;
- die $errors, "\n" ;
- }
- }
- if ( $interactive_ui ) {
- ### interactive mode
- require VCP::UI;
- my $ui = VCP::UI->new(
- defined $terse_ui_prompts ? ( TersePrompts => 1 ) : (),
- );
- print <<END_INTRO unless @plugins || defined $terse_ui_prompts;
- This is vcp's text mode interactive user interface. It asks a series of
- questions and then allows you to save the answers in a configuration
- file and/or do a conversion based on them.
- After each question any example free-form input is shown in parentheses.
- If a default value is available, that is shown in square brackets. You
- may press 'Enter' to accept the default. For yes/no questions, 'y' or
- 'n' is sufficient. For non-yes/no multiple choice questions, use the
- number or enter the entire choice text.
- vcp may also be provided with all the information it needs on the
- command line or using a configuration file. To read up on these
- options, run "vcp help" from the command line after quitting the
- interactive interface.
- END_INTRO
- my ( $source, $dest, $do_convert );
- ( $source, $dest, $output_config_file, $do_convert ) = $ui->run(
- @plugins
- ? (
- Source => $plugins[ 0],
- Dest => $plugins[-1],
- Filename => $vcp_filename,
- )
- : ()
- );
- $dont_convert = 1 unless $do_convert;
- if ( ! @plugins ) { ## None scanned from existing .vcp file
- push @plugins, $source;
- require VCP::DefaultFilters;
- my $df = VCP::DefaultFilters->new;
- my @filter_args = $df->create_default_filters( $source, $dest );
- # call load_module for each filter in @filter_args.
- while ( @filter_args ) {
- my $spec = shift @filter_args;
- my $default_scheme;
- if ( find_filter( $spec ) ) {
- push @plugins, load_module(
- $spec, "Filter", undef, \@filter_args
- );
- }
- else {
- BUG "attempted to load a non-existent default filter: $spec";
- }
- }
- push @plugins, $dest;
- }
- else {
- $plugins[ 0] = $source;
- $plugins[-1] = $dest;
- }
- }
- if ( $arg eq "scan" ) {
- @plugins = (
- $plugins[0],
- load_module( "metadb:source_metadb:", "Dest", undef, [] ),
- );
- }
- elsif ( $arg eq "filter" ) {
- shift @plugins; ## filtering requires no source and no dest
- pop @plugins;
- pr "vcp: no filters to apply\n" unless @plugins;
- @plugins = (
- load_module( "metadb:source_metadb:", "Source", undef, [] ),
- @plugins,
- load_module( "metadb:filtered_metadb:", "Dest", undef, [] ),
- );
- }
- elsif ( $arg eq "transfer" ) {
- @plugins = (
- load_module( "metadb:filtered_metadb:", "Source", undef, [
- $plugins[0],
- ] ),
- $plugins[-1],
- );
- }
- if ( defined $output_config_file ) {
- require VCP::ConfigFileUtils;
- VCP::ConfigFileUtils::write_config_file( $output_config_file, @plugins );
- }
- unless ( $dont_convert ) {
- # init should:
- # set default values don't make sense in the constructor
- # do initialization that doesn't make sense in constructor
- # do cross-checking between fields
- $_->init for @plugins;
- my $cp = VCP->new( @plugins );
- $cp->insert_required_sort_filter
- unless $arg eq "scan" || $arg eq "transfer";
- my $header = {} ;
- my $footer = {} ;
- $cp->copy_all( $header, $footer ) ;
- }
- 1;
- } or do {
- my $x = $@;
- lg $x;
- die $x;
- };
- ###############################################################################
- ###############################################################################
- sub parse_cli_options {
- local *ARGV = shift @_;
- GetOptions(
- 'debug|d' => \my $unused_1_see_BEGIN_above,
- 'dont-convert' => \$dont_convert,
- 'output-config-file=s' => \$output_config_file,
- 'help|h|?:s' => sub {
- help_and_exit( length $_[1] ? $_[1] : () );
- },
- 'profile' => \my $unused_2_see_BEGIN_above,
- 'terse|t' => \$terse_ui_prompts,
- 'versions' => \&versions_and_exit,
- ) or options_and_exit() ;
- die "--output-config-file requires a filename\n"
- if defined $output_config_file && ! length $output_config_file;
- }
- sub load_module {
- my ( $spec, $type, $default_scheme, @args ) = @_;
- $type = ucfirst $type;
- my $class = "VCP::$type";
- my ( $scheme, $s ) = $spec =~ /^(\w{2,}):/
- ? ( $1, $spec )
- : defined $default_scheme
- ? ( $default_scheme, "$default_scheme:$spec" )
- : die "vcp: '$spec' has no scheme, try ",
- list_modules( $class ),
- "\n";
- $scheme = lc($scheme);
- my $name = "${class}::$scheme";
- my $filename = $name ;
- $filename =~ s{::}{/}g ;
- my $v = eval "require '$filename.pm';" ;
- die "unknown \L$type\E scheme '$scheme:', try ",
- list_modules( $class ),
- "\n"
- if ! $v && $@ =~ /^Can't locate $filename.pm/ ;
- die $@ unless $v;
- lg "loaded '$name' from '", $INC{"$filename.pm"}, "'";
- # New should:
- # construct the object
- # parse options, if preent
- # set some default values
- # do some initialization
- my $module = $name->new( $s, @args ) ;
- return $module;
- }
- {
- my $filters;
- sub find_filter {
- my $spec = shift;
- return(
- $spec =~ /\A(\w{2,}):/ ## filters *must* have a scheme
- && do {
- my $scheme = lc $1;
- $filters ||= {
- map { ( $_ => undef ) } scan_modules( "VCP::Filter" )
- };
- exists $filters->{$scheme};
- }
- );
- }
- }
- sub scan_modules {
- my ( $prefix ) = @_ ;
- my $dirname = $prefix . '::' ;
- $dirname =~ s{(::)+}{/}g ;
- my %seen ;
- require File::Spec;
- for ( @INC ) {
- my $dir = File::Spec->catdir( $_, $dirname ) ;
- opendir( D, $dir ) or next ;
- my @files = grep $_ !~ /^\.\.?$/ && s/\.pm$//i, readdir D ;
- closedir D ;
- $seen{$_} = 1 for @files ;
- }
- return keys %seen;
- }
- sub list_modules {
- my ( $prefix ) = @_ ;
- my $list = join ', ', map "$_:", scan_modules( @_ );
- $list =~ s/,([^,]*)$/ or$1/ ;
- return $list ;
- }
- sub usage_and_exit {
- require Pod::Usage ;
- lg @_;
- Pod::Usage::pod2usage( -message => shift, -verbose => 0, -exitval => 1 ) ;
- }
- sub options_and_exit {
- lg @_;
- require Pod::Usage ;
- print STDERR "\n";
- Pod::Usage::pod2usage( -verbose => 1, -exitval => 1, -output => \*STDERR ) ;
- }
- sub find_help_modules {
- my ( $desired_module ) = @_;
- require File::Find;
- require File::Spec;
- my %modules;
- for my $inc_dir ( @INC ) {
- $inc_dir = File::Spec->rel2abs( $inc_dir );
- my $vcp_file = File::Spec->catfile( $inc_dir, "VCP.pm" );
- $modules{VCP} ||= $vcp_file if -f $vcp_file;
- my $vcp_dir = File::Spec->catdir( $inc_dir, "VCP" );
- next unless -d $vcp_dir;
- my @found;
- File::Find::find(
- sub {
- return if -d $_;
- return unless /\.(pm|pod)\Z/i;
- my $mod_name = File::Spec->abs2rel( $File::Find::name, $vcp_dir );
- $mod_name =~ s{[:\\/]+}{::}g;
- $mod_name =~ s{\.(pm|pod)}{}i;
- if ( defined $desired_module && lc $mod_name eq $desired_module ) {
- push @found, $File::Find::name;
- }
- else {
- $modules{$mod_name} ||= $File::Find::name;
- }
- },
- $vcp_dir
- );
- # .pod sorts after .pm, and we prefer to find the pod files, since
- # they are more likely to contain end user docs when both .pod
- # and .pm files exist.
- die "FOUND ", ( sort @found )[-1], "\n"
- if @found;
- }
- return %modules;
- }
- sub help_and_exit {
- require Pod::Usage ;
- my ( $topic ) = @_;
- my $result = 0;
- if ( defined $topic ) {
- $topic = lc $topic;
- if ( $topic eq "vcp" ) {
- if( $ENV{PAGER} ) {
- system( "pod2text '$0' |$ENV{PAGER}" );
- }
- else {
- system( "pod2text", $0 );
- }
- exit $result;
- }
- eval {
- find_help_modules( $topic );
- };
- if ( $@ =~ /FOUND (.*)/ ) {
- if( $ENV{PAGER} ) {
- exit system( "pod2text '$1' |$ENV{PAGER}" ) >> 8;
- }
- else {
- exit system( "pod2text", $1 ) >> 8;
- }
- }
- elsif ( $@ ) {
- die $@;
- }
- $result = 1;
- warn "Unrecognized help topic '$topic'\n";
- }
- print <<END_HELP_TOPICS;
- $program_name - Version Copy, a tool for copying versions file repositories
- help topics (use "vcp help <topic>" to see):
- vcp General help for the vcp command
- source::p4 Extracting from a p4 repository
- dest::p4 Inserting in to a p4 repository
- source::cvs Extracting from a cvs repository
- dest::cvs Inserting in to a cvs repository
- newlines Newline, ^Z and NULL issues
- process How $program_name works
- license Copyright and license information
- maintenance VCP Code maintenance, debugging tips & tricks
- The PAGER environment variable specifies pager program to use for
- these help topics.
- END_HELP_TOPICS
- exit $result;
- }
- sub build_html_tree_and_exit {
- my ( $prog_name, $dest_dir ) = @_;
- unless ( defined $dest_dir && length $dest_dir ) {
- $dest_dir = $prog_name . "_html";
- }
- require File::Spec;
- $dest_dir = File::Spec->rel2abs( $dest_dir );
- $| = 1;
- print "Generating HTML in $dest_dir/";
- my %modules = find_help_modules;
- require Pod::Links;
- require Pod::HTML_Elements;
- require File::Path;
- require IO::File;
- ## BEGIN CODE ADAPTED FROM NICK ING-SIMMONS' PodToHTML package
- my $links = Pod::Links->new();
- for my $fn (
- $0,
- grep /Filter[^.]|Source[^.]|Dest[^.]|\.pod/, values %modules
- ) {
- print ".";
- $links->parse_from_file($fn);
- }
- for my $name ($links->names) {
- $links->link(
- $name,
- do {
- my $outfile = $name;
- $outfile =~ s#::#/#g;
- $outfile =~ s#[^/a-z0-9A-Z._-]#_#g;
- $outfile .= ".html";
- File::Spec->catfile( $dest_dir, $outfile );
- }
- ) if $links->pod($name);
- }
- my $index_file = File::Spec->catfile( $dest_dir, "index.html" );
- my $parser = Pod::HTML_Elements->new(
- Index => $index_file,
- Links => $links,
- );
- ## the sort {} makes sure "vcp" is listed first in the
- ## resulting index.
- for my $name (
- sort {
- $a eq "vcp"
- ? -1
- : $b eq "vcp"
- ? 1
- : $a cmp $b
- } $links->names
- ) {
- print ".";
- my $file = $links->pod($name);
- my $outfile = $links->link($name);
- if (defined $file) {
- File::Path::mkpath( File::Basename::dirname( $outfile ), 0, 0755 );
- $parser->parse_from_file($file,$outfile);
- }
- }
- $parser->write_index;
- ## END CODE ADAPTED FROM NICK ING-SIMMONS' PodToHTML package
- print "\n";
- print "Finished, index file is $index_file\n";
- exit( 0 );
- }
- sub versions_and_exit {
- require File::Find ;
- require File::Spec;
- my $require_module = sub {
- return unless m/\.pm$/i ;
- ## Avoid "name used only once" warning
- my $fn = $File::Find::name ;
- $fn = $File::Find::name ;
- require $fn ;
- } ;
- File::Find::find(
- {
- no_chdir => 1,
- wanted => $require_module,
- },
- grep -d $_,
- map {
- ( File::Spec->catdir( $_, "lib", "VCP", "Source" ),
- File::Spec->catdir( $_, "lib", "VCP", "Dest" ),
- ) ;
- } @INC
- ) ;
- my %vers ;
- my %no_vers ;
- my $recur ;
- $recur = sub {
- my ( $pkg_namespace ) = @_ ;
- no strict "refs" ;
- my $pkg_name = substr( $pkg_namespace, 0, -2 ) ;
- ## The grep means "only bother with namespaces that contain somthing
- ## other than child namespaces.
- if ( ! grep /::/, keys %{$pkg_namespace} ) {
- if ( exists ${$pkg_namespace}{VERSION} ) {
- $vers{$pkg_name} = ${"${pkg_namespace}VERSION"}
- }
- else {
- $no_vers{$pkg_name} = undef ;
- }
- }
- my $prefix = $pkg_namespace eq "main::" ? "" : $pkg_namespace ;
- for ( keys %{$pkg_namespace} ) {
- next unless /::$/ ;
- next if /^main::/ ;
- $recur->( "$prefix$_" ) ;
- }
- } ;
- $recur->( "main::" ) ;
- my $max_len = 0 ;
- $max_len = length > $max_len ? length : $max_len for keys %vers ;
- print "Package \$VERSIONs:\n" ;
- for ( sort keys %vers ) {
- printf(
- " %-${max_len}s: %s\n",
- $_,
- defined $vers{$_} ? $vers{$_} : "undef"
- ) ;
- }
- print "No \$VERSION found for: ", join( ", ", sort keys %no_vers ), "\n" ;
- $max_len = 0 ;
- $max_len = length > $max_len ? length : $max_len for values %INC ;
- print "\nFile sizes:\n" ;
- for ( sort values %INC ) {
- printf( " %-${max_len}s: %7d\n", $_, -s $_ ) ;
- }
- print "\nperl -V:\n" ;
- my $v = `$^X -V` ;
- $v =~ s/^/ /gm ;
- print $v ;
- exit ;
- }
- =head1 SEE ALSO
- L<VCP::Process>, L<VCP::Newlines>, L<VCP::Source::p4>, L<VCP::Dest::p4>,
- L<VCP::Source::cvs>, L<VCP::Dest::cvs>, L<VCP::Source::revml>,
- L<VCP::Dest::revml>, L<VCP::Newlines>. All are also available using C<vcp
- help>.
- =head1 AUTHOR
- Barrie Slaymaker <barries@slaysys.com>
- =head1 COPYRIGHT
- Copyright (c) 2000, 2001, 2002 Perforce Software, Inc.
- All rights reserved.
- See L<VCP::License|VCP::License> (C<vcp help license>) for the terms of use.
- =cut
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 4040 | timothee_besset | guest branch | 21 years ago | |
//guest/perforce_software/revml/bin/vcp | |||||
#65 | 3855 | Barrie Slaymaker | - vcp scan, filter, transfer basically functional - Need more work in re: s...torage format, etc, but functional « |
21 years ago | |
#64 | 3779 | Barrie Slaymaker | - bin/vcp has nascent support for 3 stage transfers: - scan the source &nb...sp; - filter the metadata (modify or prune) - transfer the filtered results - PRELIMINARY « |
21 years ago | |
#63 | 3725 | Barrie Slaymaker | - Minor spelling correction | 22 years ago | |
#62 | 3697 | Barrie Slaymaker | - Write out new source if editing .vcp file changed source | 22 years ago | |
#61 | 3676 | Barrie Slaymaker |
- The user interface should no longer append a second Dest: section. |
22 years ago | |
#60 | 3672 | Barrie Slaymaker | - vcp emits more meaningful errors when fed a 0 length .vcp file | 22 years ago | |
#59 | 3666 | Barrie Slaymaker | - vcp can now edit existing .vcp files, for VSS sources and revml dests | 22 years ago | |
#58 | 3663 | Barrie Slaymaker | - Yes/no questions may be answered 'y' or 'n' (case insensitive) - Multiple choice questi...ons may be answered by typing in full text of an answer (case insensitive) « |
22 years ago | |
#57 | 3649 | Barrie Slaymaker | - Stray DB::single = 1s removed | 22 years ago | |
#56 | 3644 | Barrie Slaymaker | - Add Save & Run options to end of UI | 22 years ago | |
#55 | 3640 | Barrie Slaymaker | - xmllint no longer require to build UI - UI now offers multiple choices where appropriat...e « |
22 years ago | |
#54 | 3636 | Barrie Slaymaker | - --terse option description improved | 22 years ago | |
#53 | 3567 | John Fetkovich | - added the field UIManager in VCP::UI::Text.pm - added the fields UIImplementation and T...ersePrompts in UI.pm - removed Source and Dest fields in VCP::UI.pm - UI.pm now returns the result of running the UI implementation. - VCP::UI::Text->run return a list of (source, dest) all future UI implementations must do the same. - bin/vcp gets (source, dest) list from VCP::UI->run. - added --terse (or -t) command line option to vcp to remove verbose help from interactive UI. « |
22 years ago | |
#52 | 3548 | John Fetkovich | Load default filters when vcp interactive ui is run. | 22 years ago | |
#51 | 3495 | John Fetkovich | improved help message at start of interactive vcp | 22 years ago | |
#50 | 3488 | John Fetkovich | move init of each plugin after writing config file | 22 years ago | |
#49 | 3487 | Barrie Slaymaker | - Add --dont-convert option to bin/vcp | 22 years ago | |
#48 | 3486 | John Fetkovich | moved (source or dest)->init calls to bin/vcp | 22 years ago | |
#47 | 3481 | John Fetkovich | intro text moved out of state machine to bin/vcp. no longer re...quires user interaction to move on. « |
22 years ago | |
#46 | 3479 | John Fetkovich | doc fix | 22 years ago | |
#45 | 3468 | Barrie Slaymaker | - get --output-config-file working with ui (if ui completes, which may not happen... if the user enters invalid input). - Note various UI issue in TODO. « |
22 years ago | |
#44 | 3466 | Barrie Slaymaker | - --output-config-file now usable with vcp:- specification - --output-config-file now tes...ted - VCP::{Source,Dest}::null now sets a repo_scheme so they may be emitted to config files. - Dest: is now emitted right after Source: when there are filters to dump. « |
22 years ago | |
#43 | 3464 | Barrie Slaymaker | - Create VCP::ConfigFileUtils and move bin/vcp::parse_config_file in to it. - Add... VCP::ConfigFileUtils::write_config_file() - Add --output-vcp-file to bin/vcp. - Add VCP::Driver::repo_spec_as_string() - Add VCP::Driver::config_file_section_as_string() - VCP::Driver::parse_repo_spec() now clears any settings that are not set by a given spec string (so old values don't remain after a call to it). « |
22 years ago | |
#42 | 3441 | Barrie Slaymaker | - Add some debugging code | 22 years ago | |
#41 | 3406 | Barrie Slaymaker | - bin/vcp tries harder to keep backslashes for Win32 compat | 22 years ago | |
#40 | 3364 | John Fetkovich | clean up code flow in options parsing/interactive-ui calling section | 22 years ago | |
#39 | 3363 | John Fetkovich | cleanup code flow, remove some debugging statements | 22 years ago | |
#38 | 3362 | John Fetkovich | revml source and dest now works through interactive UI | 22 years ago | |
#37 | 3271 | John Fetkovich | Added stub init() in VCP/Plugin.pm, and call to it in load_mod...ule in bin/vcp. This is in preparation for Sources and Dests to have parts of their current constructors split out into init() functions which will facilitate setting of fields from the interative ui code. « |
22 years ago | |
#36 | 3244 | Barrie Slaymaker |
Integrate VCP::UI with bin/vcp. Type 'vcp' to run the UI. |
22 years ago | |
#35 | 3167 | Barrie Slaymaker | Add profiling report that details various chunks of time taken.... « |
22 years ago | |
#34 | 3155 | Barrie Slaymaker | Convert to logging using VCP::Logger to reduce stdout/err spew. &nbs...p;Simplify & speed up debugging quite a bit. Provide more verbose information in logs. Print to STDERR progress reports to keep users from wondering what's going on. Breaks test; halfway through upgrading run3() to an inline function for speed and for VCP specific features. « |
22 years ago | |
#33 | 3018 | Barrie Slaymaker | fix command line parsing: options were not being cleansed from... @ARGV properly. « |
22 years ago | |
#32 | 2844 | John Fetkovich | Made VCP help aware of PAGER environment variable. | 22 years ago | |
#31 | 2711 | Barrie Slaymaker | remove debugging statement, fix spurios error message | 22 years ago | |
#30 | 2708 | Barrie Slaymaker | Add support for Options: in .vcp files, improve testing of CLI | 22 years ago | |
#29 | 2705 | Barrie Slaymaker | Clean up comments | 22 years ago | |
#28 | 2688 | Barrie Slaymaker | Add back $ENV{VCPDEBUG} and augment with turning on debugging... for IPC::Run3. « |
22 years ago | |
#27 | 2639 | John Fetkovich | Added profiling to be made active when VCPPROFILE environment ...variable turned on. writes profile info to filename defined in VCPPROFILE. Put some profiling statements (activated at compile time) in vcp and p4.pm. « |
22 years ago | |
#26 | 2453 | John Fetkovich |
removed compilation of revml. will be making that a separate executable. |
22 years ago | |
#25 | 2354 | Barrie Slaymaker | favor .pod files over .pm files in the vcp help system. | 22 years ago | |
#24 | 2307 | Barrie Slaymaker | get VCP::Filter::map working, update docs | 22 years ago | |
#23 | 2306 | Barrie Slaymaker | tweak error text | 22 years ago | |
#22 | 2304 | Barrie Slaymaker | VCP::Filter::map docs and infrastructure | 22 years ago | |
#21 | 2302 | Barrie Slaymaker | test, debug filter chain CLI parsing | 22 years ago | |
#20 | 2301 | Barrie Slaymaker | A chain of plugins instead of source & dest | 22 years ago | |
#19 | 2297 | Barrie Slaymaker | Adapt cvs, revml to a more realistic branching structure | 22 years ago | |
#18 | 2293 | Barrie Slaymaker | Update CHANGES, TODO, improve .vcp files, add --init-cvs | 22 years ago | |
#17 | 2282 | Barrie Slaymaker | Add in support for *.vcp files | 22 years ago | |
#16 | 2014 | Barrie Slaymaker | Give helpful error messages if the vcp command can't read/write &nbs...p;RevML due to a missing required module. « |
23 years ago | |
#15 | 2013 | Barrie Slaymaker | Reenable vcp as a unixy command line filter processing revml | 23 years ago | |
#14 | 1759 | Barrie Slaymaker | Don't try to keep parsing cmd line after unknown scheme | 23 years ago | |
#13 | 1506 | Barrie Slaymaker | tweaks | 23 years ago | |
#12 | 1367 | Barrie Slaymaker | lots of docco updates | 23 years ago | |
#11 | 1359 | Barrie Slaymaker | Revamp the help system, clean up and add POD | 23 years ago | |
#10 | 703 | Barrie Slaymaker | VCP::Source::p4 now uses VCP::Utils::p4::parse_p4_repo_spec() | 24 years ago | |
#9 | 692 | Barrie Slaymaker | Add VCP::Utils::p4 and use it to get VCP::Dest::p4 to create it's own client view as need...ed. « |
24 years ago | |
#8 | 688 | Barrie Slaymaker | Fixed docos for --debug. | 24 years ago | |
#7 | 628 | Barrie Slaymaker | Cleaned up POD in bin/vcp, added BSD-style license. | 24 years ago | |
#6 | 627 | Barrie Slaymaker | Beef up CVS log file parsing. | 24 years ago | |
#5 | 624 | Barrie Slaymaker | Add a space to bin/vcp SYNOPSIS after the cvs -r option. | 24 years ago | |
#4 | 613 | Barrie Slaymaker | Tweak README and documentation. | 24 years ago | |
#3 | 480 | Barrie Slaymaker | 0.06 Wed Dec 20 23:19:15 EST 2000 - bin/vcp: Added --versions, which loads a...ll modules and checks them for a $VERSION and print the results out. This should help with diagnosing out-of-sync modules. - Added $VERSION vars to a few modules :-). Forgot to increment any $VERSION strings. - VCP::Dest::cvs: The directory "deeply" was not being `cvs add`ed on paths like "a/deeply/nested/file", assuming "deeply" had no files in it. - VCP::Dest::revml: fixed a bug that was causing files with a lot of linefeeds to be emitted in base64 instead of deltaed. This means most text files. - Various minor cleanups of diagnostics and error messages, including exposing "Can't locate Foo.pm" when a VCP::Source or VCP::Dest module depends on a module that's not installed, as reported by Jeff Anton. « |
24 years ago | |
#2 | 468 | Barrie Slaymaker | - VCP::Dest::p4 now does change number aggregation based on the comment field chan...ging or whenever a new revision of a file with unsubmitted changes shows up on the input stream. Since revisions of files are normally sorted in time order, this should work in a number of cases. I'm sure we'll need to generalize it, perhaps with a time thresholding function. - t/90cvs.t now tests cvs->p4 replication. - VCP::Dest::p4 now doesn't try to `p4 submit` when no changes are pending. - VCP::Rev now prevents the same label from being applied twice to a revision. This was occuring because the "r_1"-style label that gets added to a target revision by VCP::Dest::p4 could duplicate a label "r_1" that happened to already be on a revision. - Added t/00rev.t, the beginnings of a test suite for VCP::Rev. - Tweaked bin/gentrevml to comment revisions with their change number instead of using a unique comment for every revision for non-p4 t/test-*-in-0.revml files. This was necessary to test cvs->p4 functionality. « |
24 years ago | |
#1 | 467 | Barrie Slaymaker | Version 0.01, initial checkin in perforce public depot. | 24 years ago |