Merge topic 'ctest-p4'
970c823
ctest_update: Add support for Perforce p4 client
This commit is contained in:
commit
ef13fc4dfc
|
@ -149,6 +149,7 @@ if(BUILD_TESTING)
|
|||
find_program(BZRCOMMAND bzr)
|
||||
find_program(HGCOMMAND hg)
|
||||
find_program(GITCOMMAND git)
|
||||
find_program(P4COMMAND p4)
|
||||
|
||||
if(NOT UPDATE_TYPE)
|
||||
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/CVS")
|
||||
|
@ -180,6 +181,9 @@ if(BUILD_TESTING)
|
|||
elseif("${_update_type}" STREQUAL "git")
|
||||
set(UPDATE_COMMAND "${GITCOMMAND}")
|
||||
set(UPDATE_OPTIONS "${GIT_UPDATE_OPTIONS}")
|
||||
elseif("${_update_type}" STREQUAL "p4")
|
||||
set(UPDATE_COMMAND "${P4COMMAND}")
|
||||
set(UPDATE_OPTIONS "${P4_UPDATE_OPTIONS}")
|
||||
endif()
|
||||
|
||||
set(DART_TESTING_TIMEOUT 1500 CACHE STRING
|
||||
|
@ -275,6 +279,7 @@ if(BUILD_TESTING)
|
|||
CVS_UPDATE_OPTIONS
|
||||
DART_TESTING_TIMEOUT
|
||||
GITCOMMAND
|
||||
P4COMMAND
|
||||
HGCOMMAND
|
||||
MAKECOMMAND
|
||||
MEMORYCHECK_COMMAND
|
||||
|
|
|
@ -52,6 +52,13 @@ GITCommand: @GITCOMMAND@
|
|||
GITUpdateOptions: @GIT_UPDATE_OPTIONS@
|
||||
GITUpdateCustom: @CTEST_GIT_UPDATE_CUSTOM@
|
||||
|
||||
# Perforce options
|
||||
P4Command: @P4COMMAND@
|
||||
P4Client: @CTEST_P4_CLIENT@
|
||||
P4Options: @CTEST_P4_OPTIONS@
|
||||
P4UpdateOptions: @CTEST_P4_UPDATE_OPTIONS@
|
||||
P4UpdateCustom: @CTEST_P4_UPDATE_CUSTOM@
|
||||
|
||||
# Generic update command
|
||||
UpdateCommand: @UPDATE_COMMAND@
|
||||
UpdateOptions: @UPDATE_OPTIONS@
|
||||
|
|
|
@ -465,6 +465,8 @@ set(CTEST_SRCS cmCTest.cxx
|
|||
CTest/cmCTestGIT.h
|
||||
CTest/cmCTestHG.cxx
|
||||
CTest/cmCTestHG.h
|
||||
CTest/cmCTestP4.cxx
|
||||
CTest/cmCTestP4.h
|
||||
)
|
||||
|
||||
# Build CTestLib
|
||||
|
|
|
@ -0,0 +1,569 @@
|
|||
/*============================================================================
|
||||
CMake - Cross Platform Makefile Generator
|
||||
Copyright 2000-2013 Kitware, Inc.
|
||||
|
||||
Distributed under the OSI-approved BSD License (the "License");
|
||||
see accompanying file Copyright.txt for details.
|
||||
|
||||
This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the License for more information.
|
||||
============================================================================*/
|
||||
#include "cmCTestP4.h"
|
||||
|
||||
#include "cmCTest.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmXMLSafe.h"
|
||||
|
||||
#include <cmsys/RegularExpression.hxx>
|
||||
#include <cmsys/ios/sstream>
|
||||
#include <cmsys/Process.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
cmCTestP4::cmCTestP4(cmCTest* ct, std::ostream& log):
|
||||
cmCTestGlobalVC(ct, log)
|
||||
{
|
||||
this->PriorRev = this->Unknown;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
cmCTestP4::~cmCTestP4()
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class cmCTestP4::IdentifyParser: public cmCTestVC::LineParser
|
||||
{
|
||||
public:
|
||||
IdentifyParser(cmCTestP4* p4, const char* prefix,
|
||||
std::string& rev): Rev(rev)
|
||||
{
|
||||
this->SetLog(&p4->Log, prefix);
|
||||
this->RegexIdentify.compile("^Change ([0-9]+) on");
|
||||
}
|
||||
private:
|
||||
std::string& Rev;
|
||||
cmsys::RegularExpression RegexIdentify;
|
||||
|
||||
bool ProcessLine()
|
||||
{
|
||||
if(this->RegexIdentify.find(this->Line))
|
||||
{
|
||||
this->Rev = this->RegexIdentify.match(1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class cmCTestP4::ChangesParser: public cmCTestVC::LineParser
|
||||
{
|
||||
public:
|
||||
ChangesParser(cmCTestP4* p4, const char* prefix) : P4(p4)
|
||||
{
|
||||
this->SetLog(&P4->Log, prefix);
|
||||
this->RegexIdentify.compile("^Change ([0-9]+) on");
|
||||
}
|
||||
private:
|
||||
cmsys::RegularExpression RegexIdentify;
|
||||
cmCTestP4* P4;
|
||||
|
||||
bool ProcessLine()
|
||||
{
|
||||
if(this->RegexIdentify.find(this->Line))
|
||||
{
|
||||
P4->ChangeLists.push_back(this->RegexIdentify.match(1));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class cmCTestP4::UserParser: public cmCTestVC::LineParser
|
||||
{
|
||||
public:
|
||||
UserParser(cmCTestP4* p4, const char* prefix) : P4(p4)
|
||||
{
|
||||
this->SetLog(&P4->Log, prefix);
|
||||
this->RegexUser.compile("^(.+) <(.*)> \\((.*)\\) accessed (.*)$");
|
||||
}
|
||||
private:
|
||||
cmsys::RegularExpression RegexUser;
|
||||
cmCTestP4* P4;
|
||||
|
||||
bool ProcessLine()
|
||||
{
|
||||
if(this->RegexUser.find(this->Line))
|
||||
{
|
||||
User NewUser;
|
||||
|
||||
NewUser.UserName = this->RegexUser.match(1);
|
||||
NewUser.EMail = this->RegexUser.match(2);
|
||||
NewUser.Name = this->RegexUser.match(3);
|
||||
NewUser.AccessTime = this->RegexUser.match(4);
|
||||
P4->Users[this->RegexUser.match(1)] = NewUser;
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/* Diff format:
|
||||
==== //depot/file#rev - /absolute/path/to/file ====
|
||||
(diff data)
|
||||
==== //depot/file2#rev - /absolute/path/to/file2 ====
|
||||
(diff data)
|
||||
==== //depot/file3#rev - /absolute/path/to/file3 ====
|
||||
==== //depot/file4#rev - /absolute/path/to/file4 ====
|
||||
(diff data)
|
||||
*/
|
||||
class cmCTestP4::DiffParser: public cmCTestVC::LineParser
|
||||
{
|
||||
public:
|
||||
DiffParser(cmCTestP4* p4, const char* prefix)
|
||||
: P4(p4), AlreadyNotified(false)
|
||||
{
|
||||
this->SetLog(&P4->Log, prefix);
|
||||
this->RegexDiff.compile("^==== (.*)#[0-9]+ - (.*)");
|
||||
}
|
||||
private:
|
||||
cmCTestP4* P4;
|
||||
bool AlreadyNotified;
|
||||
std::string CurrentPath;
|
||||
cmsys::RegularExpression RegexDiff;
|
||||
|
||||
bool ProcessLine()
|
||||
{
|
||||
if(!this->Line.empty() && this->Line[0] == '='
|
||||
&& this->RegexDiff.find(this->Line))
|
||||
{
|
||||
std::string Path = this->RegexDiff.match(1);
|
||||
// See if we need to remove the //depot prefix
|
||||
if(Path.length() > 2 && Path[0] == '/' && Path[1] == '/')
|
||||
{
|
||||
size_t found = Path.find('/', 2);
|
||||
if(found != std::string::npos)
|
||||
{
|
||||
Path = Path.substr(found + 1);
|
||||
}
|
||||
}
|
||||
CurrentPath = Path;
|
||||
AlreadyNotified = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!AlreadyNotified)
|
||||
{
|
||||
P4->DoModification(PathModified, CurrentPath);
|
||||
AlreadyNotified = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
cmCTestP4::User cmCTestP4::GetUserData(const std::string& username)
|
||||
{
|
||||
std::map<std::string, cmCTestP4::User>::const_iterator it =
|
||||
Users.find(username);
|
||||
|
||||
if(it == Users.end())
|
||||
{
|
||||
std::vector<char const*> p4_users;
|
||||
SetP4Options(p4_users);
|
||||
p4_users.push_back("users");
|
||||
p4_users.push_back("-m");
|
||||
p4_users.push_back("1");
|
||||
p4_users.push_back(username.c_str());
|
||||
p4_users.push_back(0);
|
||||
|
||||
UserParser out(this, "users-out> ");
|
||||
OutputLogger err(this->Log, "users-err> ");
|
||||
RunChild(&p4_users[0], &out, &err);
|
||||
|
||||
// The user should now be added to the map. Search again.
|
||||
it = Users.find(username);
|
||||
if(it == Users.end())
|
||||
{
|
||||
return cmCTestP4::User();
|
||||
}
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/* Commit format:
|
||||
|
||||
Change 1111111 by user@client on 2013/09/26 11:50:36
|
||||
|
||||
text
|
||||
text
|
||||
|
||||
Affected files ...
|
||||
|
||||
... //path/to/file#rev edit
|
||||
... //path/to/file#rev add
|
||||
... //path/to/file#rev delete
|
||||
... //path/to/file#rev integrate
|
||||
*/
|
||||
class cmCTestP4::DescribeParser: public cmCTestVC::LineParser
|
||||
{
|
||||
public:
|
||||
DescribeParser(cmCTestP4* p4, const char* prefix):
|
||||
LineParser('\n', false), P4(p4), Section(SectionHeader)
|
||||
{
|
||||
this->SetLog(&P4->Log, prefix);
|
||||
this->RegexHeader.compile("^Change ([0-9]+) by (.+)@(.+) on (.*)$");
|
||||
this->RegexDiff.compile("^\\.\\.\\. (.*)#[0-9]+ ([^ ]+)$");
|
||||
}
|
||||
private:
|
||||
cmsys::RegularExpression RegexHeader;
|
||||
cmsys::RegularExpression RegexDiff;
|
||||
cmCTestP4* P4;
|
||||
|
||||
typedef cmCTestP4::Revision Revision;
|
||||
typedef cmCTestP4::Change Change;
|
||||
std::vector<Change> Changes;
|
||||
enum SectionType { SectionHeader, SectionBody, SectionDiffHeader,
|
||||
SectionDiff, SectionCount };
|
||||
SectionType Section;
|
||||
Revision Rev;
|
||||
|
||||
virtual bool ProcessLine()
|
||||
{
|
||||
if(this->Line.empty())
|
||||
{
|
||||
this->NextSection();
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(this->Section)
|
||||
{
|
||||
case SectionHeader: this->DoHeaderLine(); break;
|
||||
case SectionBody: this->DoBodyLine(); break;
|
||||
case SectionDiffHeader: break; // nothing to do
|
||||
case SectionDiff: this->DoDiffLine(); break;
|
||||
case SectionCount: break; // never happens
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NextSection()
|
||||
{
|
||||
if(this->Section == SectionDiff)
|
||||
{
|
||||
this->P4->DoRevision(this->Rev, this->Changes);
|
||||
this->Rev = Revision();
|
||||
}
|
||||
|
||||
this->Section = SectionType((this->Section+1) % SectionCount);
|
||||
}
|
||||
|
||||
void DoHeaderLine()
|
||||
{
|
||||
if(this->RegexHeader.find(this->Line))
|
||||
{
|
||||
this->Rev.Rev = this->RegexHeader.match(1);
|
||||
this->Rev.Date = this->RegexHeader.match(4);
|
||||
|
||||
cmCTestP4::User user = P4->GetUserData(this->RegexHeader.match(2));
|
||||
this->Rev.Author = user.Name;
|
||||
this->Rev.EMail = user.EMail;
|
||||
|
||||
this->Rev.Committer = this->Rev.Author;
|
||||
this->Rev.CommitterEMail = this->Rev.EMail;
|
||||
this->Rev.CommitDate = this->Rev.Date;
|
||||
}
|
||||
}
|
||||
|
||||
void DoBodyLine()
|
||||
{
|
||||
if(this->Line[0] == '\t')
|
||||
{
|
||||
this->Rev.Log += this->Line.substr(1);
|
||||
}
|
||||
this->Rev.Log += "\n";
|
||||
}
|
||||
|
||||
void DoDiffLine()
|
||||
{
|
||||
if(this->RegexDiff.find(this->Line))
|
||||
{
|
||||
Change change;
|
||||
std::string Path = this->RegexDiff.match(1);
|
||||
if(Path.length() > 2 && Path[0] == '/' && Path[1] == '/')
|
||||
{
|
||||
size_t found = Path.find('/', 2);
|
||||
if(found != std::string::npos)
|
||||
{
|
||||
Path = Path.substr(found + 1);
|
||||
}
|
||||
}
|
||||
|
||||
change.Path = Path;
|
||||
std::string action = this->RegexDiff.match(2);
|
||||
|
||||
if(action == "add")
|
||||
{
|
||||
change.Action = 'A';
|
||||
}
|
||||
else if(action == "delete")
|
||||
{
|
||||
change.Action = 'D';
|
||||
}
|
||||
else if(action == "edit" || action == "integrate")
|
||||
{
|
||||
change.Action = 'M';
|
||||
}
|
||||
|
||||
Changes.push_back(change);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void cmCTestP4::SetP4Options(std::vector<char const*> &CommandOptions)
|
||||
{
|
||||
if(P4Options.size() == 0)
|
||||
{
|
||||
const char* p4 = this->CommandLineTool.c_str();
|
||||
P4Options.push_back(p4);
|
||||
|
||||
//The CTEST_P4_CLIENT variable sets the P4 client used when issuing
|
||||
//Perforce commands, if it's different from the default one.
|
||||
std::string client = this->CTest->GetCTestConfiguration("P4Client");
|
||||
if(!client.empty())
|
||||
{
|
||||
P4Options.push_back("-c");
|
||||
P4Options.push_back(client);
|
||||
}
|
||||
|
||||
//Set the message language to be English, in case the P4 admin
|
||||
//has localized them
|
||||
P4Options.push_back("-L");
|
||||
P4Options.push_back("en");
|
||||
|
||||
//The CTEST_P4_OPTIONS variable adds additional Perforce command line
|
||||
//options before the main command
|
||||
std::string opts = this->CTest->GetCTestConfiguration("P4Options");
|
||||
std::vector<cmStdString> args =
|
||||
cmSystemTools::ParseArguments(opts.c_str());
|
||||
|
||||
for(std::vector<cmStdString>::const_iterator ai = args.begin();
|
||||
ai != args.end(); ++ai)
|
||||
{
|
||||
P4Options.push_back(ai->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
CommandOptions.clear();
|
||||
for(std::vector<std::string>::iterator i = P4Options.begin();
|
||||
i != P4Options.end(); ++i)
|
||||
{
|
||||
CommandOptions.push_back(i->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
std::string cmCTestP4::GetWorkingRevision()
|
||||
{
|
||||
std::vector<char const*> p4_identify;
|
||||
SetP4Options(p4_identify);
|
||||
|
||||
p4_identify.push_back("changes");
|
||||
p4_identify.push_back("-m");
|
||||
p4_identify.push_back("1");
|
||||
p4_identify.push_back("-t");
|
||||
|
||||
std::string source = this->SourceDirectory + "/...#have";
|
||||
p4_identify.push_back(source.c_str());
|
||||
p4_identify.push_back(0);
|
||||
|
||||
std::string rev;
|
||||
IdentifyParser out(this, "rev-out> ", rev);
|
||||
OutputLogger err(this->Log, "rev-err> ");
|
||||
|
||||
RunChild(&p4_identify[0], &out, &err);
|
||||
|
||||
if(rev.empty())
|
||||
{
|
||||
return "0";
|
||||
}
|
||||
else
|
||||
{
|
||||
return rev;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void cmCTestP4::NoteOldRevision()
|
||||
{
|
||||
this->OldRevision = this->GetWorkingRevision();
|
||||
|
||||
cmCTestLog(this->CTest, HANDLER_OUTPUT, " Old revision of repository is: "
|
||||
<< this->OldRevision << "\n");
|
||||
this->PriorRev.Rev = this->OldRevision;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void cmCTestP4::NoteNewRevision()
|
||||
{
|
||||
this->NewRevision = this->GetWorkingRevision();
|
||||
|
||||
cmCTestLog(this->CTest, HANDLER_OUTPUT, " New revision of repository is: "
|
||||
<< this->NewRevision << "\n");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void cmCTestP4::LoadRevisions()
|
||||
{
|
||||
std::vector<char const*> p4_changes;
|
||||
SetP4Options(p4_changes);
|
||||
|
||||
// Use 'p4 changes ...@old,new' to get a list of changelists
|
||||
std::string range = this->SourceDirectory + "/...";
|
||||
|
||||
if(this->OldRevision != "0")
|
||||
{
|
||||
range.append("@").append(this->OldRevision);
|
||||
}
|
||||
|
||||
if(this->NewRevision != "0")
|
||||
{
|
||||
if(this->OldRevision != "0")
|
||||
{
|
||||
range.append(",").append(this->NewRevision);
|
||||
}
|
||||
else
|
||||
{
|
||||
range.append("@").append(this->NewRevision);
|
||||
}
|
||||
}
|
||||
|
||||
p4_changes.push_back("changes");
|
||||
p4_changes.push_back(range.c_str());
|
||||
p4_changes.push_back(0);
|
||||
|
||||
ChangesParser out(this, "changes-out> ");
|
||||
OutputLogger err(this->Log, "changes-err> ");
|
||||
|
||||
ChangeLists.clear();
|
||||
this->RunChild(&p4_changes[0], &out, &err);
|
||||
|
||||
if(ChangeLists.size() == 0)
|
||||
return;
|
||||
|
||||
//p4 describe -s ...@1111111,2222222
|
||||
std::vector<char const*> p4_describe;
|
||||
for(std::vector<std::string>::reverse_iterator i = ChangeLists.rbegin();
|
||||
i != ChangeLists.rend(); ++i)
|
||||
{
|
||||
SetP4Options(p4_describe);
|
||||
p4_describe.push_back("describe");
|
||||
p4_describe.push_back("-s");
|
||||
p4_describe.push_back(i->c_str());
|
||||
p4_describe.push_back(0);
|
||||
|
||||
DescribeParser outDescribe(this, "describe-out> ");
|
||||
OutputLogger errDescribe(this->Log, "describe-err> ");
|
||||
this->RunChild(&p4_describe[0], &outDescribe, &errDescribe);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void cmCTestP4::LoadModifications()
|
||||
{
|
||||
std::vector<char const*> p4_diff;
|
||||
SetP4Options(p4_diff);
|
||||
|
||||
p4_diff.push_back("diff");
|
||||
|
||||
//Ideally we would use -Od but not all clients support it
|
||||
p4_diff.push_back("-dn");
|
||||
std::string source = this->SourceDirectory + "/...";
|
||||
p4_diff.push_back(source.c_str());
|
||||
p4_diff.push_back(0);
|
||||
|
||||
DiffParser out(this, "diff-out> ");
|
||||
OutputLogger err(this->Log, "diff-err> ");
|
||||
this->RunChild(&p4_diff[0], &out, &err);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool cmCTestP4::UpdateCustom(const std::string& custom)
|
||||
{
|
||||
std::vector<std::string> p4_custom_command;
|
||||
cmSystemTools::ExpandListArgument(custom, p4_custom_command, true);
|
||||
|
||||
std::vector<char const*> p4_custom;
|
||||
for(std::vector<std::string>::const_iterator
|
||||
i = p4_custom_command.begin(); i != p4_custom_command.end(); ++i)
|
||||
{
|
||||
p4_custom.push_back(i->c_str());
|
||||
}
|
||||
p4_custom.push_back(0);
|
||||
|
||||
OutputLogger custom_out(this->Log, "custom-out> ");
|
||||
OutputLogger custom_err(this->Log, "custom-err> ");
|
||||
|
||||
return this->RunUpdateCommand(&p4_custom[0], &custom_out, &custom_err);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool cmCTestP4::UpdateImpl()
|
||||
{
|
||||
std::string custom = this->CTest->GetCTestConfiguration("P4UpdateCustom");
|
||||
if(!custom.empty())
|
||||
{
|
||||
return this->UpdateCustom(custom);
|
||||
}
|
||||
|
||||
std::vector<char const*> p4_sync;
|
||||
SetP4Options(p4_sync);
|
||||
|
||||
p4_sync.push_back("sync");
|
||||
|
||||
// Get user-specified update options.
|
||||
std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
|
||||
if(opts.empty())
|
||||
{
|
||||
opts = this->CTest->GetCTestConfiguration("P4UpdateOptions");
|
||||
}
|
||||
std::vector<cmStdString> args = cmSystemTools::ParseArguments(opts.c_str());
|
||||
for(std::vector<cmStdString>::const_iterator ai = args.begin();
|
||||
ai != args.end(); ++ai)
|
||||
{
|
||||
p4_sync.push_back(ai->c_str());
|
||||
}
|
||||
|
||||
std::string source = this->SourceDirectory + "/...";
|
||||
|
||||
// Specify the start time for nightly testing.
|
||||
if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
|
||||
{
|
||||
std::string date = this->GetNightlyTime();
|
||||
//CTest reports the date as YYYY-MM-DD, Perforce needs it as YYYY/MM/DD
|
||||
std::replace(date.begin(), date.end(), '-', '/');
|
||||
|
||||
//Revision specification: /...@"YYYY/MM/DD HH:MM:SS"
|
||||
source.append("@\"").append(date).append("\"");
|
||||
}
|
||||
|
||||
p4_sync.push_back(source.c_str());
|
||||
p4_sync.push_back(0);
|
||||
|
||||
OutputLogger out(this->Log, "sync-out> ");
|
||||
OutputLogger err(this->Log, "sync-err> ");
|
||||
|
||||
return this->RunUpdateCommand(&p4_sync[0], &out, &err);
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*============================================================================
|
||||
CMake - Cross Platform Makefile Generator
|
||||
Copyright 2000-2013 Kitware, Inc.
|
||||
|
||||
Distributed under the OSI-approved BSD License (the "License");
|
||||
see accompanying file Copyright.txt for details.
|
||||
|
||||
This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the License for more information.
|
||||
============================================================================*/
|
||||
#ifndef cmCTestP4_h
|
||||
#define cmCTestP4_h
|
||||
|
||||
#include "cmCTestGlobalVC.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
/** \class cmCTestP4
|
||||
* \brief Interaction with the Perforce command-line tool
|
||||
*
|
||||
*/
|
||||
class cmCTestP4: public cmCTestGlobalVC
|
||||
{
|
||||
public:
|
||||
/** Construct with a CTest instance and update log stream. */
|
||||
cmCTestP4(cmCTest* ctest, std::ostream& log);
|
||||
|
||||
virtual ~cmCTestP4();
|
||||
|
||||
private:
|
||||
std::vector<std::string> ChangeLists;
|
||||
|
||||
struct User
|
||||
{
|
||||
std::string UserName;
|
||||
std::string Name;
|
||||
std::string EMail;
|
||||
std::string AccessTime;
|
||||
|
||||
User(): UserName(), Name(), EMail(), AccessTime() {}
|
||||
};
|
||||
std::map<std::string, User> Users;
|
||||
std::vector<std::string> P4Options;
|
||||
|
||||
User GetUserData(const std::string& username);
|
||||
void SetP4Options(std::vector<char const*> &options);
|
||||
|
||||
std::string GetWorkingRevision();
|
||||
virtual void NoteOldRevision();
|
||||
virtual void NoteNewRevision();
|
||||
virtual bool UpdateImpl();
|
||||
bool UpdateCustom(const std::string& custom);
|
||||
|
||||
void LoadRevisions();
|
||||
void LoadModifications();
|
||||
|
||||
// Parsing helper classes.
|
||||
class IdentifyParser;
|
||||
class ChangesParser;
|
||||
class UserParser;
|
||||
class DescribeParser;
|
||||
class DiffParser;
|
||||
friend class IdentifyParser;
|
||||
friend class ChangesParser;
|
||||
friend class UserParser;
|
||||
friend class DescribeParser;
|
||||
friend class DiffParser;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -59,6 +59,14 @@ cmCTestGenericHandler* cmCTestUpdateCommand::InitializeHandler()
|
|||
"HGCommand", "CTEST_HG_COMMAND");
|
||||
this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
|
||||
"HGUpdateOptions", "CTEST_HG_UPDATE_OPTIONS");
|
||||
this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
|
||||
"P4Command", "CTEST_P4_COMMAND");
|
||||
this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
|
||||
"P4UpdateOptions", "CTEST_P4_UPDATE_OPTIONS");
|
||||
this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
|
||||
"P4Client", "CTEST_P4_CLIENT");
|
||||
this->CTest->SetCTestConfigurationFromCMakeVariable(this->Makefile,
|
||||
"P4Options", "CTEST_P4_OPTIONS");
|
||||
|
||||
cmCTestGenericHandler* handler
|
||||
= this->CTest->GetInitializedHandler("update");
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "cmCTestBZR.h"
|
||||
#include "cmCTestGIT.h"
|
||||
#include "cmCTestHG.h"
|
||||
#include "cmCTestP4.h"
|
||||
|
||||
#include <cmsys/auto_ptr.hxx>
|
||||
|
||||
|
@ -51,7 +52,8 @@ static const char* cmCTestUpdateHandlerUpdateStrings[] =
|
|||
"SVN",
|
||||
"BZR",
|
||||
"GIT",
|
||||
"HG"
|
||||
"HG",
|
||||
"P4"
|
||||
};
|
||||
|
||||
static const char* cmCTestUpdateHandlerUpdateToString(int type)
|
||||
|
@ -146,6 +148,10 @@ int cmCTestUpdateHandler::DetermineType(const char* cmd, const char* type)
|
|||
{
|
||||
return cmCTestUpdateHandler::e_HG;
|
||||
}
|
||||
if ( stype.find("p4") != std::string::npos )
|
||||
{
|
||||
return cmCTestUpdateHandler::e_P4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -172,6 +178,10 @@ int cmCTestUpdateHandler::DetermineType(const char* cmd, const char* type)
|
|||
{
|
||||
return cmCTestUpdateHandler::e_HG;
|
||||
}
|
||||
if ( stype.find("p4") != std::string::npos )
|
||||
{
|
||||
return cmCTestUpdateHandler::e_P4;
|
||||
}
|
||||
}
|
||||
return cmCTestUpdateHandler::e_UNKNOWN;
|
||||
}
|
||||
|
@ -223,6 +233,7 @@ int cmCTestUpdateHandler::ProcessHandler()
|
|||
case e_BZR: vc.reset(new cmCTestBZR(this->CTest, ofs)); break;
|
||||
case e_GIT: vc.reset(new cmCTestGIT(this->CTest, ofs)); break;
|
||||
case e_HG: vc.reset(new cmCTestHG(this->CTest, ofs)); break;
|
||||
case e_P4: vc.reset(new cmCTestP4(this->CTest, ofs)); break;
|
||||
default: vc.reset(new cmCTestVC(this->CTest, ofs)); break;
|
||||
}
|
||||
vc->SetCommandLineTool(this->UpdateCommand);
|
||||
|
@ -350,6 +361,18 @@ int cmCTestUpdateHandler::DetectVCS(const char* dir)
|
|||
{
|
||||
return cmCTestUpdateHandler::e_HG;
|
||||
}
|
||||
sourceDirectory = dir;
|
||||
sourceDirectory += "/.p4";
|
||||
if ( cmSystemTools::FileExists(sourceDirectory.c_str()) )
|
||||
{
|
||||
return cmCTestUpdateHandler::e_P4;
|
||||
}
|
||||
sourceDirectory = dir;
|
||||
sourceDirectory += "/.p4config";
|
||||
if ( cmSystemTools::FileExists(sourceDirectory.c_str()) )
|
||||
{
|
||||
return cmCTestUpdateHandler::e_P4;
|
||||
}
|
||||
return cmCTestUpdateHandler::e_UNKNOWN;
|
||||
}
|
||||
|
||||
|
@ -380,6 +403,7 @@ bool cmCTestUpdateHandler::SelectVCS()
|
|||
case e_BZR: key = "BZRCommand"; break;
|
||||
case e_GIT: key = "GITCommand"; break;
|
||||
case e_HG: key = "HGCommand"; break;
|
||||
case e_P4: key = "P4Command"; break;
|
||||
default: break;
|
||||
}
|
||||
if (key)
|
||||
|
|
|
@ -44,6 +44,7 @@ public:
|
|||
e_BZR,
|
||||
e_GIT,
|
||||
e_HG,
|
||||
e_P4,
|
||||
e_LAST
|
||||
};
|
||||
|
||||
|
|
|
@ -1877,6 +1877,26 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
|
|||
)
|
||||
list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${CTestUpdateHG_DIR}")
|
||||
endif()
|
||||
|
||||
# Test CTest Update with P4
|
||||
find_program(P4_EXECUTABLE NAMES p4)
|
||||
find_program(P4D_EXECUTABLE NAMES p4d)
|
||||
mark_as_advanced(P4_EXECUTABLE P4D_EXECUTABLE)
|
||||
set(CTEST_TEST_UPDATE_P4 0)
|
||||
if(P4_EXECUTABLE AND P4D_EXECUTABLE)
|
||||
if(NOT "${P4_EXECUTABLE};${P4D_EXECUTABLE}" MATCHES "cygwin" OR UNIX)
|
||||
set(CTEST_TEST_UPDATE_P4 1)
|
||||
endif()
|
||||
endif()
|
||||
if(CTEST_TEST_UPDATE_P4)
|
||||
set(CTestUpdateP4_DIR "CTest UpdateP4")
|
||||
configure_file("${CMake_SOURCE_DIR}/Tests/CTestUpdateP4.cmake.in"
|
||||
"${CMake_BINARY_DIR}/Tests/CTestUpdateP4.cmake" @ONLY)
|
||||
add_test(CTest.UpdateP4 ${CMAKE_CMAKE_COMMAND}
|
||||
-P "${CMake_BINARY_DIR}/Tests/CTestUpdateP4.cmake"
|
||||
)
|
||||
list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${CTestUpdateP4_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
configure_file(
|
||||
|
|
|
@ -216,7 +216,7 @@ function(run_dashboard_script bin_dir)
|
|||
)
|
||||
|
||||
# Verify the updates reported by CTest.
|
||||
list(APPEND UPDATE_MAYBE Updated{subdir})
|
||||
list(APPEND UPDATE_MAYBE Updated{subdir} Updated{CTestConfig.cmake})
|
||||
check_updates(${bin_dir}
|
||||
Updated{foo.txt}
|
||||
Updated{bar.txt}
|
||||
|
|
|
@ -0,0 +1,260 @@
|
|||
# This script drives creation of a perforce repository and checks
|
||||
# that CTest can update from it.
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Test in a directory next to this script.
|
||||
get_filename_component(TOP "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
set(P4_TOP "${TOP}")
|
||||
set(TOP "${TOP}/@CTestUpdateP4_DIR@")
|
||||
|
||||
# Include code common to all update tests.
|
||||
include("@CMAKE_CURRENT_SOURCE_DIR@/CTestUpdateCommon.cmake")
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Perforce server options
|
||||
set(P4_HOST localhost)
|
||||
set(P4_PORT 1888)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Report p4 tools in use and set its defaults
|
||||
message("Using P4 tools:")
|
||||
set(P4 "@P4_EXECUTABLE@")
|
||||
set(P4D "@P4D_EXECUTABLE@")
|
||||
message(" p4 = ${P4}")
|
||||
message(" p4d = ${P4D}")
|
||||
|
||||
set(P4_CLIENT -c ctest_p4)
|
||||
set(P4_OPTIONS -H ${P4_HOST} -p ${P4_PORT})
|
||||
set(P4CMD ${P4} ${P4_OPTIONS})
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Start the Perforce server
|
||||
if(UNIX)
|
||||
set(P4_ROOT ${P4_TOP}/perforce)
|
||||
|
||||
message("Starting p4d on '${P4_ROOT}' listening on port ${P4_PORT}...")
|
||||
|
||||
# Stop a previous instance of Perforce running
|
||||
execute_process(
|
||||
WORKING_DIRECTORY ${TOP}
|
||||
COMMAND ${P4CMD} admin stop
|
||||
OUTPUT_QUIET
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
# Make sure we don't have a perforce directory from a previous run
|
||||
file(REMOVE_RECURSE ${P4_ROOT})
|
||||
file(MAKE_DIRECTORY ${P4_ROOT})
|
||||
|
||||
set(P4_SERVER "nohup '${P4D}' -d -r '${P4_ROOT}'")
|
||||
set(P4_SERVER "${P4_SERVER} -L '${P4_ROOT}/p4.log'")
|
||||
set(P4_SERVER "${P4_SERVER} -J '${P4_ROOT}/journal'")
|
||||
set(P4_SERVER "${P4_SERVER} -p ${P4_PORT} >/dev/null 2>&1 &")
|
||||
|
||||
message("Server command line: ${P4_SERVER}")
|
||||
|
||||
execute_process(
|
||||
COMMAND sh -c "
|
||||
${P4_SERVER}
|
||||
for i in 1 2 3 4 5 6 7 8 9 10; do
|
||||
echo 'Waiting for server to start...'
|
||||
sleep 1
|
||||
if '${P4}' -H ${P4_HOST} -p ${P4_PORT} help >/dev/null 2>&1; then
|
||||
echo 'Server started.'
|
||||
exit
|
||||
fi
|
||||
done
|
||||
echo 'Gave up waiting for server to start.'
|
||||
"
|
||||
)
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Initialize the testing directory.
|
||||
message("Creating test directory...")
|
||||
init_testing()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Create the repository.
|
||||
message("Creating depot...")
|
||||
file(WRITE ${TOP}/depot.spec "Depot: ctest\n")
|
||||
file(APPEND ${TOP}/depot.spec "Type: local\n")
|
||||
file(APPEND ${TOP}/depot.spec "Map: ctest/...\n")
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}
|
||||
COMMAND ${P4CMD} depot -i
|
||||
INPUT_FILE ${TOP}/depot.spec
|
||||
)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Import initial content into the repository.
|
||||
message("Importing content...")
|
||||
create_content(user-source)
|
||||
|
||||
message("Creating client spec...")
|
||||
file(WRITE ${TOP}/client.spec "Client: ctest_p4\n")
|
||||
file(APPEND ${TOP}/client.spec "Root: ${TOP}/user-source\n")
|
||||
file(APPEND ${TOP}/client.spec "View: //ctest/... //ctest_p4/...\n")
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} client -i
|
||||
INPUT_FILE ${TOP}/client.spec
|
||||
)
|
||||
|
||||
# After creating the depot and the client view, all P4 commands need to
|
||||
# have the client spec passed to them
|
||||
list(APPEND P4CMD ${P4_CLIENT})
|
||||
|
||||
message("Adding files to repository")
|
||||
file(GLOB_RECURSE files ${TOP}/user-source/*)
|
||||
foreach(filename ${files})
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} add ${filename}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
message("Submitting changes to repository")
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} submit -d "CTEST: Initial content"
|
||||
)
|
||||
message("Tagging the repository")
|
||||
file(WRITE ${TOP}/label.spec "Label: r1\n")
|
||||
file(APPEND ${TOP}/label.spec "View: //ctest/...\n")
|
||||
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} label -i
|
||||
INPUT_FILE ${TOP}/label.spec
|
||||
)
|
||||
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} labelsync -l r1
|
||||
)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Make changes in the working tree.
|
||||
message("Changing content...")
|
||||
update_content(user-source files_added files_removed dirs_added)
|
||||
foreach(filename ${files_added})
|
||||
message("add: ${filename}")
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} add ${TOP}/user-source/${filename}
|
||||
)
|
||||
endforeach()
|
||||
foreach(filename ${files_removed})
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} delete ${TOP}/user-source/${filename}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Commit the changes to the repository.
|
||||
message("Committing revision 2...")
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} submit -d "CTEST: Changed content"
|
||||
)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Make changes in the working tree.
|
||||
message("Changing content again...")
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} edit //ctest/...
|
||||
)
|
||||
|
||||
change_content(user-source)
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} revert -a //ctest/...
|
||||
)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Commit the changes to the repository.
|
||||
message("Committing revision 3...")
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} submit -d "CTEST: Changed content again"
|
||||
)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Go back to before the changes so we can test updating.
|
||||
message("Backing up to revision 1...")
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} sync @r1
|
||||
)
|
||||
|
||||
# Create a modified file.
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} sync @r1
|
||||
)
|
||||
|
||||
# We should p4 open any files that modify_content creates
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} open ${TOP}/user-source/CTestConfig.cmake
|
||||
)
|
||||
modify_content(user-source)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Test updating the user work directory with the command-line interface.
|
||||
message("Running CTest Dashboard Command Line...")
|
||||
|
||||
# Create the user build tree.
|
||||
create_build_tree(user-source user-binary)
|
||||
file(APPEND ${TOP}/user-binary/CTestConfiguration.ini
|
||||
"# P4 command configuration
|
||||
UpdateCommand: ${P4}
|
||||
P4Client: ctest_p4
|
||||
P4Options: -H ${P4_HOST} -p ${P4_PORT}
|
||||
")
|
||||
|
||||
# Run the dashboard command line interface.
|
||||
run_dashboard_command_line(user-binary)
|
||||
|
||||
# Revert the modified files
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}/user-source
|
||||
COMMAND ${P4CMD} revert ${TOP}/user-source/CTestConfig.cmake
|
||||
)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Test initial checkout and update with a dashboard script.
|
||||
# Create a new client so we can check out files on a different directory
|
||||
message("Running CTest Dashboard Script...")
|
||||
|
||||
message("Creating client spec...")
|
||||
file(WRITE ${TOP}/client2.spec "Client: ctest2_p4\n")
|
||||
file(APPEND ${TOP}/client2.spec "Root: ${TOP}/dash-source\n")
|
||||
file(APPEND ${TOP}/client2.spec "View: //ctest/... //ctest2_p4/...\n")
|
||||
run_child(
|
||||
COMMAND ${P4CMD} client -i
|
||||
INPUT_FILE ${TOP}/client2.spec
|
||||
)
|
||||
|
||||
file(MAKE_DIRECTORY ${TOP}/dash-source)
|
||||
|
||||
create_dashboard_script(dash-binary
|
||||
"# P4 command configuration
|
||||
set(CTEST_P4_CLIENT \"ctest2_p4\")
|
||||
set(CTEST_P4_OPTIONS \"-H ${P4_HOST} -p ${P4_PORT}\")
|
||||
set(CTEST_UPDATE_COMMAND \"${P4}\")
|
||||
")
|
||||
|
||||
# Run the dashboard script with CTest.
|
||||
run_dashboard_script(dash-binary)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Clean up
|
||||
message("Shutting down p4d")
|
||||
run_child(
|
||||
WORKING_DIRECTORY ${TOP}
|
||||
COMMAND ${P4CMD} admin stop
|
||||
)
|
Loading…
Reference in New Issue