package MinP4;
=head1 NAME
MinP4.pm - Minimal Perforce interface.
=head1 VERSION
1.3.1
=head1 DESCRIPTION
Minimal Perforce interface, not to be confused with the more comprehensive P4Perl API (the P4.pm module).
=head1 PUBLIC DATA
None.
=head1 PUBLIC FUNCTIONS
=cut
require Exporter;
use strict;
use File::Temp;
use POSIX qw(uname);
use Misc;
use Msg;
use Cmd;
# The next line avoids problems with 'use strict'.
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
# Initialization processing occurs in the BEGIN block.
BEGIN
{
# Keep $VERSION value the same as at the top of this file.
$VERSION = "1.3.1";
}
# Package interface standards. By default, any export can be blocked.
@ISA = qw(Exporter);
@EXPORT_OK = qw(
FindJIRAIssues
GenChangelist
GenGlobalWorkspace
GenStreamWorkspace
GenWorkspace
GetBranchPaths
GetChangelistFiles
);
# Prototypes for public functions.
sub FindJIRAIssues ($$;);
sub GenChangelist($$$$;);
sub GenGlobalWorkspace($$$;);
sub GenStreamWorkspace($$$$;);
sub GenWorkspace($$$$;);
sub GetBranchPaths($$$;$);
sub GetChangelistFiles($$;);
#==============================================================================
# Internal Functions
#==============================================================================
#==============================================================================
# Public Functions
#==============================================================================
#------------------------------------------------------------------------------
# Find JIRA issues in the changelist description.
=head2 FindJIRAIssues()
FindJIRAIssues($I<change_desc>, $I<issueArrayRef>)
=head3 Description-FindJIRAIssues
Find JIRA issue keys in a Perforce changelist description.
=head3 Parameters-FindJIRAIssues
=over 4
=item *
$I<changelist>
=item *
$I<issueArrayRef>
A reference to an array containing a list of issues.
=back
=head3 Output-FindJIRAIssues
None.
=head3 Returns-FindJIRAIssues
None.
=head3 Examples-FindJIRAIssues
Sample call:
C<MinP4::FindJIRAIssues ($change_desc, \@issuesList);>
=cut
#------------------------------------------------------------------------------
sub FindJIRAIssues ($$;)
{
$Msg->trace ("CALL MinP4::FindJIRAIssues(@_)");
my ($changeDesc, $issueArrayRef) = @_;
my $issue;
my $issueCount = 0;
foreach (split '\n', $changeDesc) {
foreach (split ' ', $_) {
next if (/\[\s*review/i);
next if (/\#review/i);
if (/[A-Z]{1}([A-Z]|[0-9])+\-\d+/i) {
# We found a JIRA issue. Clean up the text, removing
# whitespace and text following the numnbers.
s/\s//g;
s/(\.|\;|\:).*$//g;
# Normalize the JIRA issue to uppercase,
# then capture it.
$_ = uc($_); # Normalize to uppercase.
@$issueArrayRef[$issueCount++]=$_;
}
}
}
}
#------------------------------------------------------------------------------
=head2 GenChangelist()
GenChangelist($I<P4PORT>, $I<P4USER>, $I<P4CLIENT>, $I<Desc>;)
=head3 Description-GenChangelist
Generate a Perforce changelist.
=head3 Parameters-GenChangelist
=over 4
=item *
$I<P4PORT>
=item *
$I<P4USER>
=item *
$I<P4CLIENT>
=item *
$I<Desc> - Description for the generated Perforce changelist, quoted.
=back
=head3 Output-GenChangelist
None except for errors.
=head3 Returns-GenChangelist
This returns the generated changelist number.
In NoOp mode, '999999' is returned.
In event of error, a 0 is returned.
=head3 Examples-GenChangelist
Sample call:
C<my $cl = MinP4::GenChangelist ("perforce:1666", "perforce", "perforce.demo_ws", "This is my description.");>
C<die "Error!" if ($cl == 0);>
=cut
#------------------------------------------------------------------------------
sub GenChangelist($$$$;)
{
$Msg->trace ("CALL MinP4::GenChangelist(@_)\n");
my ($P4PORT, $P4USER, $P4CLIENT, $desc) = @_;
my $tmpFile;
my $changelist = 0;
my $changeSpec;
if ($NoOp) {
$changelist = "999999";
return $changelist;
}
$tmpFile = Misc::GenTempFilename();
$changeSpec = "Change: new\n\nClient: $P4CLIENT\n\nUser: $P4USER\n\nStatus: new\n\nDescription:\n\t$desc\n\n";
open (TMP, ">$tmpFile") or $Msg->logdie ("Error: Failed to create temp file [$tmpFile]: $!\n");
print TMP $changeSpec;
close (TMP);
Cmd::Run ("/p4/common/bin/p4 -p $P4PORT -u $P4USER -c $P4CLIENT -s change -i < $tmpFile",
"Generating pending changelist.", 0, $INFO);
if ($Cmd::Output =~ /info: Change \d+/) {
$changelist = $Cmd::Output;
$changelist =~ s/^.*info: Change //;
$changelist =~ s/ .*$//s;
} else {
$Msg->error ("Error generating changelist. Spec:\n$changeSpec\n\nOutput:\n$Cmd::Output\n");
return 0;
}
return $changelist;
}
#------------------------------------------------------------------------------
=head2 GenWorkspace()
GenWorkspace($I<P4PORT>, $I<P4USER>, $I<P4CLIENT>, $I<path>;)
=head3 Description-GenWorkspace
Generate a Perforce workspace. Honors $NoOp and $Verbosity. In $NoOp mode, the workspace spec is shown, but the workspace is not created on the Perforce server.
=head3 Parameters-GenWorkspace
=over 4
=item *
$I<P4PORT>
=item *
$I<P4USER>
=item *
$I<P4CLIENT>
=item *
$I<path> - A View path in Perforce depot syntax. It should start with "//some_depot", and may contain more folders. A "/..." will be appended to the value specified.
=back
=head3 Output-GenWorkspace
Displays a message indicating workspace creation, or an error message.
Honors $Verbosity setting.
=head3 Returns-GenWorkspace
None.
=head3 Examples-GenWorkspace
Sample call:
C<MinP4::GenWorkspace ("perforce:1666", "perforce", "perforce.demo_ws", "//here/there") or $Msg-E<gt>logdie ("Failed to create workspace.");>
=cut
#------------------------------------------------------------------------------
sub GenWorkspace($$$$;)
{
$Msg->trace ("CALL MinP4::GenWorkspace(@_)\n");
my ($P4PORT, $P4USER, $P4CLIENT, $path) = @_;
my $tmpFile;
my $clientSpec;
my $hostname = `hostname`;
chomp $hostname;
$clientSpec = "Client: $P4CLIENT\n\nOwner: $P4USER\n\nHost: $hostname\n\nDescription:\n\tGenerated workspace.\n\nRoot: /p4/1/tmp/ws/$P4CLIENT\n\nOptions: noallwrite noclobber nocompress unlocked modtime rmdir\n\nSubmitOptions: revertunchanged\n\nLineEnd: local\n\nView:\n\t$path/... //$P4CLIENT/...\n\n";
if ($NoOp) {
$Msg->debug ("No-Op: Would have generated workspace with this spec:\n$clientSpec\n");
return 1;
}
$tmpFile = Misc::GenTempFilename();
open (TMP, ">$tmpFile") or $Msg->logdie ("Error: Failed to create temp file [$tmpFile]: $!\n");
print TMP $clientSpec;
close (TMP);
Cmd::Run ("/p4/common/bin/p4 -p $P4PORT -u $P4USER -c $P4CLIENT -s client -i < $tmpFile",
"Generating workspace $P4CLIENT.", 0, $INFO);
if ($Cmd::Output =~ /exit: 0/) {
$Msg->info ("Generated workspace [$P4CLIENT].");
return 1;
} else {
$Msg->error ("Failed to generate workspace [$P4CLIENT].\n\nSpec:\n$clientSpec\n\nOutput:\n$Cmd::Output\n\n");
return 0;
}
}
#------------------------------------------------------------------------------
=head2 GenGlobalWorkspace()
GenGlobalWorkspace($I<P4PORT>, $I<P4USER>, $I<P4CLIENT>;)
=head3 Description-GenGlobalWorkspace
Generate a Perforce workspace with a wide view, seeing all depots of type 'local' the user has access to. Honors $NoOp and $Verbosity. In $NoOp mode, the workspace spec is shown, but the workspace is not created on the Perforce server.
=head3 Parameters-GenGlobalWorkspace
=over 4
=item *
$I<P4PORT>
=item *
$I<P4USER>
=item *
$I<P4CLIENT>
=back
=head3 Output-GenGlobalWorkspace
Displays a message indicating workspace creation, or an error message.
Honors $Verbosity setting.
=head3 Returns-GenGlobalWorkspace
None.
=head3 Examples-GenGlobalWorkspace
Sample call:
C<MinP4::GenGlobalWorkspace ("perforce:1666", "perforce", "perforce.all.demo_ws") or $Msg-E<gt>logdie ("Failed to create global workspace.");>
=cut
sub GenGlobalWorkspace($$$;)
{
$Msg->trace ("CALL MinP4::GenGlobalWorkspace(@_)\n");
my ($P4PORT, $P4USER, $P4CLIENT) = @_;
my $tmpFile;
my $clientSpec;
my $depot;
my $hostname = `hostname`;
chomp $hostname;
$clientSpec = "Client: $P4CLIENT\n\nOwner: $P4USER\n\nHost: $hostname\n\nDescription:\n\tGenerated workspace.\n\nRoot: /p4/1/tmp/ws/$P4CLIENT\n\nOptions: noallwrite noclobber nocompress unlocked modtime rmdir\n\nSubmitOptions: revertunchanged\n\nLineEnd: local\n\nView:\n";
# Get the list of all local depots, but don't display it even in high verbosity mode.
Cmd::Run ("/p4/common/bin/p4 -p $P4PORT -u $P4USER -c none -s depots",
"Getting list of local depots visible to user $P4USER.", 1, $TRACE);
foreach (split '\n', $Cmd::Output) {
next unless /^info: Depot .* \d{4}\/\d{2}\/\d{2} local /;
s/^info: Depot //;
s/ .*$//;
chomp;
$depot = $_;
$clientSpec = "$clientSpec\t//$depot/... //$P4CLIENT/$depot/...\n";
}
$clientSpec = "$clientSpec\n";
if ($NoOp) {
$Msg->debug ("No-Op: Would have generated workspace with this spec:\n$clientSpec\n");
return 1;
}
$tmpFile = Misc::GenTempFilename();
open (TMP, ">$tmpFile") or $Msg->logdie ("Error: Failed to create temp file [$tmpFile]: $!\n");
print TMP $clientSpec;
close (TMP);
Cmd::Run ("/p4/common/bin/p4 -p $P4PORT -u $P4USER -c $P4CLIENT -s client -i < $tmpFile",
"Generating workspace [$P4CLIENT].", 0, $INFO);
if ($Cmd::Output =~ /exit: 0/) {
$Msg->info ("Generated workspace [$P4CLIENT].");
return 1;
} else {
$Msg->error ("Failed to generate workspace [$P4CLIENT].\n\nSpec:\n$clientSpec\n\nOutput:\n$Cmd::Output\n\n");
return 0;
}
}
#------------------------------------------------------------------------------
=head2 GenStreamWorkspace()
GenStreamWorkspace($I<P4PORT>, $I<P4USER>, $I<P4CLIENT>, $I<stream>;)
=head3 Description-GenStreamWorkspace
Generate a Perforce workspace with a wide view, seeing all depots of type 'local' the user has access to. Honors $NoOp and $Verbosity. In $NoOp mode, the workspace spec is shown, but the workspace is not created on the Perforce server.
=head3 Parameters-GenStreamWorkspace
=over 4
=item *
$I<P4PORT>
=item *
$I<P4USER>
=item *
$I<P4CLIENT>
=item *
$I<stream>
=back
=head3 Output-GenStreamWorkspace
Displays a message indicating workspace creation, or an error message.
Honors $Verbosity setting.
=head3 Returns-GenStreamWorkspace
None.
=head3 Examples-GenStreamWorkspace
Sample call:
C<MinP4::GenStreamWorkspace ("perforce:1666", "perforce", "perforce.all.demo_ws", "//fgs/main/") or $Msg-E<gt>logdie ("Failed to create global workspace.");>
=cut
sub GenStreamWorkspace($$$$;)
{
$Msg->trace ("CALL MinP4::GenStreamWorkspace(@_)\n");
my ($P4PORT, $P4USER, $P4CLIENT, $stream) = @_;
my $tmpFile;
my $clientSpec;
my $hostname = `hostname`;
chomp $hostname;
$clientSpec = "Client: $P4CLIENT\n\nOwner: $P4USER\n\nHost: $hostname\n\nDescription:\n\tGenerated workspace.\n\nRoot: /p4/1/tmp/ws/$P4CLIENT\n\nOptions: noallwrite noclobber nocompress unlocked modtime rmdir\n\nSubmitOptions: revertunchanged\n\nLineEnd: local\n\nStream: $stream\n\n";
if ($NoOp) {
$Msg->debug ("No-Op: Would have generated workspace with this spec:\n$clientSpec\n");
return 1;
}
$tmpFile = Misc::GenTempFilename();
open (TMP, ">$tmpFile") or $Msg->logdie ("Error: Failed to create temp file [$tmpFile]: $!\n");
print TMP $clientSpec;
close (TMP);
Cmd::Run ("/p4/common/bin/p4 -p $P4PORT -u $P4USER -c $P4CLIENT -s client -i < $tmpFile",
"Generating workspace [$P4CLIENT] for stream [$stream].", 0, $INFO);
if ($Cmd::Output =~ /exit: 0/) {
$Msg->info ("Generated stream workspace [$P4CLIENT].");
return 1;
} else {
$Msg->error ("Failed to generate workspace [$P4CLIENT].\n\nSpec:\n$clientSpec\n\nOutput:\n$Cmd::Output\n\n");
return 0;
}
}
#------------------------------------------------------------------------------
=head2 GetBranchPaths()
GetBranchPaths ($I<branchSpecName>, $I<leftOrRight>, \@I<fileArrayRef>, $I<keepExclusions>)
=head3 Description-GetBranchPaths
Populate an array with source or target paths of a branch spec.
This routine properly handles paths with spaces in the name.
=head3 Parameters-GetBranchPaths
=over 4
=item *
$I<branch_spec>
The name of the branch spec.
=item *
$I<leftOrRight>
Pass 1 for paths on the left side of the branch spec, or 2 for paths on the right.
=item *
\@I<fileArrayRef>
Pass in a reference to the array of paths to populate.
=item *
$I<keep_exclusions>
Set to 1 to keep Exclusionary mappings in the reivew returned. By default, they are excluded.
=back
=head3 Returns-GetBranchPaths
Returns 1 on and populates the paths array, or 0 on error.
=head3 Examples-GetBranchPaths
=head4 Get Source Branch Paths:
C<my @files;>
C<my $branch = "dev_FS247";>
C<MinP4::GetBranchPaths ($branchName, 1, \@files) or $Msg-E<gt>logdie ("Couldn't get branch paths.");>
=head4 Get Target Branch Paths:
C<my @files;>
C<my $branch = "dev_FS247";>
C<MinP4::GetBranchPaths ($branchName, 2, \@files);>
=cut
#------------------------------------------------------------------------------
sub GetBranchPaths ($$$;$)
{
$Msg->trace("CALL MinP4::GetBranchPaths (@_)");
my ($branchSpecName, $sourceOrTarget, $pathsArrayRef, $keepExclusions) = @_;
my $path;
my $i = 0;
Cmd::Run ("p4 -ztag branch -o $branchSpecName",
"Getting details for branch spec [$branchSpecName]", 1, $DEBUG);
if ($Cmd::Output =~ /\.\.\. Access /) {
if ($sourceOrTarget == 1) {
foreach (split ('\n', $Cmd::Output)) {
next unless (/^\.\.\. View\d+ /);
s/^\.\.\. View\d+ //;
if (/^\-/ or /^\"\-/) {
next unless ($keepExclusions);
}
# Consider entries with and without spaces needing double
# quotes, and with and without exclusionary mappings.
if (/\"\-\/\//) {
s/^.*\"\/\//\"\-\/\//;
} elsif (/\-\/\//) {
s/^.*\-\/\//\-\/\//;
} elsif (/\"\/\//) {
s/^.*\"\-\/\//\"\-\/\//;
} else {
s/^.*\/\//\/\//;
}
#if (/\"/) { $_ = "\"$_"; }
$Msg->trace ("Adding source path [$_].\n");
@$pathsArrayRef[$i++] = $_;
}
} else {
foreach (split ('\n', $Cmd::Output)) {
next unless (/^\.\.\. View\d+ /);
s/^\.\.\. View\d+ //;
s/ \"*\/\/.*$//s;
if (/^\-/ or /^\"\-/) {
next unless ($keepExclusions);
}
$Msg->trace ("Adding target path [$_].\n");
@$pathsArrayRef[$i++] = $_;
}
}
} elsif ($Cmd::Output =~ /\.\.\. Branch /) {
$Msg->error ("Branch [$branchSpecName] does not exist.");
return 0;
} else {
$Msg->error ("Failed to process branch spec [$branchSpecName]:\n$Cmd::Output\n");
return 0;
}
return 1;
}
#------------------------------------------------------------------------------
=head2 GetChangelistFiles()
GetChangelistFiles ($I<changeInfo>, \@I<fileArrayRef>; $I<keepExclusions>)
=head3 Description-GetChangelistFiles
Populate an array with file paths from a changelist.
This routine properly handles paths with spaces in the name.
=head3 Parameters-GetChangelistFiles
=over 4
=item *
$I<changeInfo>
Provide 'p4 describe' output changelist info obtained by a command like the this sample:
C<p4 -ztag describe -s 30434>
=item *
=item *
\@I<fileArrayRef>
Pass in a reference to the array of paths to populate.
=item *
$I<keep_exclusions>
Set to 1 to keep Exclusionary mappings in the returned. By default, they are excluded.
=back
=head3 Returns-GetChangelistFiles
Returns 1 on and populates the paths array, or 0 on error.
=head3 Examples-GetChangelistFiles
=head4 Get changes from change 31110
C<my @files;>
C<my @changelist = "31110";>
C<Cmd::Run("p4 -ztag describe -s $changelist");>
C<MinP4::GetChangelistFiles ($Cmd::Output, \@files) or $Msg-E<gt>logdie ("Couldn't get files paths.");>
=cut
#------------------------------------------------------------------------------
sub GetChangelistFiles ($$;)
{
$Msg->trace("CALL MinP4::GetChangelistFiles (@_)");
my ($changelist, $filesArrayRef) = @_;
my $file;
my $i = 0;
Cmd::Run ("p4 -ztag describe -s $changelist",
"Getting changelist data.", 1, $DEBUG);
if ($Cmd::Output =~ /\.\.\. change /) {
foreach (split ('\n', $Cmd::Output)) {
next unless /^\.\.\. depotFile\d+ /;
$file = $_;
$file =~ s/^\.\.\. depotFile\d+ //;
chomp $file;
@$filesArrayRef[$i++] = $file;
}
} else {
$Msg->logdie ("Could not get details for change %changelist:\n$Cmd::Output\nAborting\n");
}
if ($Cmd::Verbosity >= $DEBUG) {
$Msg->debug("Changelist files:");
foreach (@$filesArrayRef) {
print "$_\n";
}
}
}
# Return package load success.
1;
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #6 | 27331 | C. Thomas Tyler |
Released SDP 2020.1.27325 (2021/01/29). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
| #5 | 21723 | C. Thomas Tyler |
Released SDP 2017.1.21720 (2017/02/17). Copy Up using 'p4 copy -r -b perforce_software-sdp-dev'. |
||
| #4 | 16372 | C. Thomas Tyler |
Follow on changes related to changing default admin user from 'p4admin' to 'perforce' in change in change 16370. //guest/perforce_software/sdp/main/Server/Unix/p4/common/bin/triggers/command_block.py # edit //guest/perforce_software/sdp/main/Server/Unix/p4/common/bin/triggers/keep_group_unset.py # edit //guest/perforce_software/sdp/main/Server/Unix/p4/common/lib/MinP4.pm # edit //guest/perforce_software/sdp/main/Server/Windows/setup/sdp_master_config.ini # edit //guest/perforce_software/sdp/main/Server/test/test_SDP.py # edit //guest/perforce_software/sdp/main/Server/test/test_SDPEnv.py # edit |
||
| #3 | 15856 | C. Thomas Tyler |
Replaced the big license comment block with a shortened form referencing the LICENSE file included with the SDP package, and also by the URL for the license file in The Workshop. |
||
| #2 | 13587 | C. Thomas Tyler |
Copy up of recent minor dev branch changes to auxiliary scripts and comments. No core SDP functionality changes. |
||
| #1 | 10148 | C. Thomas Tyler | Promoted the Perforce Server Deployment Package to The Workshop. |