mirror of
https://github.com/reactos/CMake.git
synced 2024-12-14 23:29:57 +00:00
96afb12087
This converts the CMake license to a pure 3-clause OSI-approved BSD License. We drop the previous license clause requiring modified versions to be plainly marked. We also update the CMake copyright to cover the full development time range.
521 lines
16 KiB
C++
521 lines
16 KiB
C++
/*============================================================================
|
|
CMake - Cross Platform Makefile Generator
|
|
Copyright 2000-2009 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 "cmCTestBZR.h"
|
|
|
|
#include "cmCTest.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmXMLParser.h"
|
|
#include "cmXMLSafe.h"
|
|
|
|
#include <cmsys/RegularExpression.hxx>
|
|
|
|
#include <cm_expat.h>
|
|
|
|
//----------------------------------------------------------------------------
|
|
extern "C"
|
|
int cmBZRXMLParserUnknownEncodingHandler(void*,
|
|
const XML_Char *name,
|
|
XML_Encoding *info)
|
|
{
|
|
static const int latin1[]=
|
|
{
|
|
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
|
|
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
|
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
|
0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
|
|
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
|
0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
|
|
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
|
0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
|
|
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
|
0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
|
|
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
|
0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
|
|
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
|
0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
|
|
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
|
0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
|
|
0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
|
|
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F,
|
|
0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
|
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178,
|
|
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
|
0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
|
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
|
0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
|
|
0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
|
|
0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
|
|
0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
|
|
0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
|
|
0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
|
|
0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
|
|
0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
|
|
0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
|
|
};
|
|
|
|
// The BZR xml output plugin can use some encodings that are not
|
|
// recognized by expat. This will lead to an error, e.g. "Error
|
|
// parsing bzr log xml: unknown encoding", the following is a
|
|
// workaround for these unknown encodings.
|
|
if(name == std::string("ascii") || name == std::string("cp1252") ||
|
|
name == std::string("ANSI_X3.4-1968"))
|
|
{
|
|
for(unsigned int i=0;i<256;++i) info->map[i] = latin1[i];
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
cmCTestBZR::cmCTestBZR(cmCTest* ct, std::ostream& log):
|
|
cmCTestGlobalVC(ct, log)
|
|
{
|
|
this->PriorRev = this->Unknown;
|
|
// Even though it is specified in the documention, with bzr 1.13
|
|
// BZR_PROGRESS_BAR has no effect. In the future this bug might be fixed.
|
|
// Since it doesn't hurt, we specify this environment variable.
|
|
cmSystemTools::PutEnv("BZR_PROGRESS_BAR=none");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
cmCTestBZR::~cmCTestBZR()
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
class cmCTestBZR::InfoParser: public cmCTestVC::LineParser
|
|
{
|
|
public:
|
|
InfoParser(cmCTestBZR* bzr, const char* prefix):
|
|
BZR(bzr), CheckOutFound(false)
|
|
{
|
|
this->SetLog(&bzr->Log, prefix);
|
|
this->RegexCheckOut.compile("checkout of branch: *([^\t\r\n]+)$");
|
|
this->RegexParent.compile("parent branch: *([^\t\r\n]+)$");
|
|
}
|
|
private:
|
|
cmCTestBZR* BZR;
|
|
bool CheckOutFound;
|
|
cmsys::RegularExpression RegexCheckOut;
|
|
cmsys::RegularExpression RegexParent;
|
|
virtual bool ProcessLine()
|
|
{
|
|
if(this->RegexCheckOut.find(this->Line))
|
|
{
|
|
this->BZR->URL = this->RegexCheckOut.match(1);
|
|
CheckOutFound = true;
|
|
}
|
|
else if(!CheckOutFound && this->RegexParent.find(this->Line))
|
|
{
|
|
this->BZR->URL = this->RegexParent.match(1);
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
class cmCTestBZR::RevnoParser: public cmCTestVC::LineParser
|
|
{
|
|
public:
|
|
RevnoParser(cmCTestBZR* bzr, const char* prefix, std::string& rev):
|
|
BZR(bzr), Rev(rev)
|
|
{
|
|
this->SetLog(&bzr->Log, prefix);
|
|
this->RegexRevno.compile("^([0-9]+)$");
|
|
}
|
|
private:
|
|
cmCTestBZR* BZR;
|
|
std::string& Rev;
|
|
cmsys::RegularExpression RegexRevno;
|
|
virtual bool ProcessLine()
|
|
{
|
|
if(this->RegexRevno.find(this->Line))
|
|
{
|
|
this->Rev = this->RegexRevno.match(1);
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::string cmCTestBZR::LoadInfo()
|
|
{
|
|
// Run "bzr info" to get the repository info from the work tree.
|
|
const char* bzr = this->CommandLineTool.c_str();
|
|
const char* bzr_info[] = {bzr, "info", 0};
|
|
InfoParser iout(this, "info-out> ");
|
|
OutputLogger ierr(this->Log, "info-err> ");
|
|
this->RunChild(bzr_info, &iout, &ierr);
|
|
|
|
// Run "bzr revno" to get the repository revision number from the work tree.
|
|
const char* bzr_revno[] = {bzr, "revno", 0};
|
|
std::string rev;
|
|
RevnoParser rout(this, "revno-out> ", rev);
|
|
OutputLogger rerr(this->Log, "revno-err> ");
|
|
this->RunChild(bzr_revno, &rout, &rerr);
|
|
|
|
return rev;
|
|
}
|
|
|
|
void cmCTestBZR::NoteOldRevision()
|
|
{
|
|
this->OldRevision = this->LoadInfo();
|
|
this->Log << "Revision before update: " << this->OldRevision << "\n";
|
|
cmCTestLog(this->CTest, HANDLER_OUTPUT, " Old revision of repository is: "
|
|
<< this->OldRevision << "\n");
|
|
this->PriorRev.Rev = this->OldRevision;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void cmCTestBZR::NoteNewRevision()
|
|
{
|
|
this->NewRevision = this->LoadInfo();
|
|
this->Log << "Revision after update: " << this->NewRevision << "\n";
|
|
cmCTestLog(this->CTest, HANDLER_OUTPUT, " New revision of repository is: "
|
|
<< this->NewRevision << "\n");
|
|
this->Log << "URL = " << this->URL << "\n";
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
class cmCTestBZR::LogParser: public cmCTestVC::OutputLogger,
|
|
private cmXMLParser
|
|
{
|
|
public:
|
|
LogParser(cmCTestBZR* bzr, const char* prefix):
|
|
OutputLogger(bzr->Log, prefix), BZR(bzr),
|
|
EmailRegex("(.*) <([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+)>")
|
|
{ this->InitializeParser(); }
|
|
~LogParser() { this->CleanupParser(); }
|
|
|
|
virtual int InitializeParser()
|
|
{
|
|
int res = cmXMLParser::InitializeParser();
|
|
if (res)
|
|
{
|
|
XML_SetUnknownEncodingHandler(static_cast<XML_Parser>(this->Parser),
|
|
cmBZRXMLParserUnknownEncodingHandler, 0);
|
|
}
|
|
return res;
|
|
}
|
|
private:
|
|
cmCTestBZR* BZR;
|
|
|
|
typedef cmCTestBZR::Revision Revision;
|
|
typedef cmCTestBZR::Change Change;
|
|
Revision Rev;
|
|
std::vector<Change> Changes;
|
|
Change CurChange;
|
|
std::vector<char> CData;
|
|
|
|
cmsys::RegularExpression EmailRegex;
|
|
|
|
virtual bool ProcessChunk(const char* data, int length)
|
|
{
|
|
this->OutputLogger::ProcessChunk(data, length);
|
|
this->ParseChunk(data, length);
|
|
return true;
|
|
}
|
|
|
|
virtual void StartElement(const char* name, const char**)
|
|
{
|
|
this->CData.clear();
|
|
if(strcmp(name, "log") == 0)
|
|
{
|
|
this->Rev = Revision();
|
|
this->Changes.clear();
|
|
}
|
|
// affected-files can contain blocks of
|
|
// modified, unknown, renamed, kind-changed, removed, conflicts, added
|
|
else if(strcmp(name, "modified") == 0
|
|
|| strcmp(name, "renamed") == 0
|
|
|| strcmp(name, "kind-changed") == 0)
|
|
{
|
|
this->CurChange = Change();
|
|
this->CurChange.Action = 'M';
|
|
}
|
|
else if(strcmp(name, "added") == 0)
|
|
{
|
|
this->CurChange = Change();
|
|
this->CurChange = 'A';
|
|
}
|
|
else if(strcmp(name, "removed") == 0)
|
|
{
|
|
this->CurChange = Change();
|
|
this->CurChange = 'D';
|
|
}
|
|
else if(strcmp(name, "unknown") == 0
|
|
|| strcmp(name, "conflicts") == 0)
|
|
{
|
|
// Should not happen here
|
|
this->CurChange = Change();
|
|
}
|
|
}
|
|
|
|
virtual void CharacterDataHandler(const char* data, int length)
|
|
{
|
|
this->CData.insert(this->CData.end(), data, data+length);
|
|
}
|
|
|
|
virtual void EndElement(const char* name)
|
|
{
|
|
if(strcmp(name, "log") == 0)
|
|
{
|
|
this->BZR->DoRevision(this->Rev, this->Changes);
|
|
}
|
|
else if((strcmp(name, "file") == 0 || strcmp(name, "directory") == 0)
|
|
&& !this->CData.empty())
|
|
{
|
|
this->CurChange.Path.assign(&this->CData[0], this->CData.size());
|
|
this->Changes.push_back(this->CurChange);
|
|
}
|
|
else if(strcmp(name, "symlink") == 0 && !this->CData.empty())
|
|
{
|
|
// symlinks have an arobase at the end in the log
|
|
this->CurChange.Path.assign(&this->CData[0], this->CData.size()-1);
|
|
this->Changes.push_back(this->CurChange);
|
|
}
|
|
else if(strcmp(name, "committer") == 0 && !this->CData.empty())
|
|
{
|
|
this->Rev.Author.assign(&this->CData[0], this->CData.size());
|
|
if(this->EmailRegex.find(this->Rev.Author))
|
|
{
|
|
this->Rev.Author = this->EmailRegex.match(1);
|
|
//email = email_regex.match(2);
|
|
}
|
|
}
|
|
else if(strcmp(name, "timestamp") == 0 && !this->CData.empty())
|
|
{
|
|
this->Rev.Date.assign(&this->CData[0], this->CData.size());
|
|
}
|
|
else if(strcmp(name, "message") == 0 && !this->CData.empty())
|
|
{
|
|
this->Rev.Log.assign(&this->CData[0], this->CData.size());
|
|
}
|
|
else if(strcmp(name, "revno") == 0 && !this->CData.empty())
|
|
{
|
|
this->Rev.Rev.assign(&this->CData[0], this->CData.size());
|
|
}
|
|
this->CData.clear();
|
|
}
|
|
|
|
virtual void ReportError(int, int, const char* msg)
|
|
{
|
|
this->BZR->Log << "Error parsing bzr log xml: " << msg << "\n";
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
class cmCTestBZR::UpdateParser: public cmCTestVC::LineParser
|
|
{
|
|
public:
|
|
UpdateParser(cmCTestBZR* bzr, const char* prefix): BZR(bzr)
|
|
{
|
|
this->SetLog(&bzr->Log, prefix);
|
|
this->RegexUpdate.compile("^([-+R?XCP ])([NDKM ])([* ]) +(.+)$");
|
|
}
|
|
private:
|
|
cmCTestBZR* BZR;
|
|
cmsys::RegularExpression RegexUpdate;
|
|
|
|
virtual bool ProcessChunk(const char* first, int length)
|
|
{
|
|
bool last_is_new_line = (*first == '\r' || *first == '\n');
|
|
|
|
const char* const last = first + length;
|
|
for(const char* c = first; c != last; ++c)
|
|
{
|
|
if(*c == '\r' || *c == '\n')
|
|
{
|
|
if(!last_is_new_line)
|
|
{
|
|
// Log this line.
|
|
if(this->Log && this->Prefix)
|
|
{
|
|
*this->Log << this->Prefix << this->Line << "\n";
|
|
}
|
|
|
|
// Hand this line to the subclass implementation.
|
|
if(!this->ProcessLine())
|
|
{
|
|
this->Line = "";
|
|
return false;
|
|
}
|
|
|
|
this->Line = "";
|
|
last_is_new_line = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Append this character to the line under construction.
|
|
this->Line.append(1, *c);
|
|
last_is_new_line = false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ProcessLine()
|
|
{
|
|
if(this->RegexUpdate.find(this->Line))
|
|
{
|
|
this->DoPath(this->RegexUpdate.match(1)[0],
|
|
this->RegexUpdate.match(2)[0],
|
|
this->RegexUpdate.match(3)[0],
|
|
this->RegexUpdate.match(4));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DoPath(char c0, char c1, char c2, std::string const& path)
|
|
{
|
|
if(path.empty()) return;
|
|
|
|
const std::string dir = cmSystemTools::GetFilenamePath(path);
|
|
const std::string name = cmSystemTools::GetFilenameName(path);
|
|
|
|
if ( c0=='C' )
|
|
{
|
|
this->BZR->Dirs[dir][name].Status = PathConflicting;
|
|
return;
|
|
}
|
|
|
|
if ( c1=='M' || c1=='K' || c1=='N' || c1=='D' || c2 =='*' )
|
|
{
|
|
this->BZR->Dirs[dir][name].Status = PathUpdated;
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmCTestBZR::UpdateImpl()
|
|
{
|
|
// Get user-specified update options.
|
|
std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
|
|
if(opts.empty())
|
|
{
|
|
opts = this->CTest->GetCTestConfiguration("BZRUpdateOptions");
|
|
}
|
|
std::vector<cmStdString> args = cmSystemTools::ParseArguments(opts.c_str());
|
|
|
|
// TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
|
|
|
|
// Use "bzr pull" to update the working tree.
|
|
std::vector<char const*> bzr_update;
|
|
bzr_update.push_back(this->CommandLineTool.c_str());
|
|
bzr_update.push_back("pull");
|
|
|
|
for(std::vector<cmStdString>::const_iterator ai = args.begin();
|
|
ai != args.end(); ++ai)
|
|
{
|
|
bzr_update.push_back(ai->c_str());
|
|
}
|
|
|
|
bzr_update.push_back(this->URL.c_str());
|
|
|
|
bzr_update.push_back(0);
|
|
|
|
// For some reason bzr uses stderr to display the update status.
|
|
OutputLogger out(this->Log, "pull-out> ");
|
|
UpdateParser err(this, "pull-err> ");
|
|
return this->RunUpdateCommand(&bzr_update[0], &out, &err);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void cmCTestBZR::LoadRevisions()
|
|
{
|
|
cmCTestLog(this->CTest, HANDLER_OUTPUT,
|
|
" Gathering version information (one . per revision):\n"
|
|
" " << std::flush);
|
|
|
|
// We are interested in every revision included in the update.
|
|
this->Revisions.clear();
|
|
std::string revs;
|
|
if(atoi(this->OldRevision.c_str()) <= atoi(this->NewRevision.c_str()))
|
|
{
|
|
// DoRevision takes care of discarding the information about OldRevision
|
|
revs = this->OldRevision + ".." + this->NewRevision;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Run "bzr log" to get all global revisions of interest.
|
|
const char* bzr = this->CommandLineTool.c_str();
|
|
const char* bzr_log[] = {bzr, "log", "-v", "-r", revs.c_str(), "--xml",
|
|
this->URL.c_str(), 0};
|
|
{
|
|
LogParser out(this, "log-out> ");
|
|
OutputLogger err(this->Log, "log-err> ");
|
|
this->RunChild(bzr_log, &out, &err);
|
|
}
|
|
cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
class cmCTestBZR::StatusParser: public cmCTestVC::LineParser
|
|
{
|
|
public:
|
|
StatusParser(cmCTestBZR* bzr, const char* prefix): BZR(bzr)
|
|
{
|
|
this->SetLog(&bzr->Log, prefix);
|
|
this->RegexStatus.compile("^([-+R?XCP ])([NDKM ])([* ]) +(.+)$");
|
|
}
|
|
private:
|
|
cmCTestBZR* BZR;
|
|
cmsys::RegularExpression RegexStatus;
|
|
bool ProcessLine()
|
|
{
|
|
if(this->RegexStatus.find(this->Line))
|
|
{
|
|
this->DoPath(this->RegexStatus.match(1)[0],
|
|
this->RegexStatus.match(2)[0],
|
|
this->RegexStatus.match(3)[0],
|
|
this->RegexStatus.match(4));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DoPath(char c0, char c1, char c2, std::string const& path)
|
|
{
|
|
if(path.empty()) return;
|
|
|
|
if ( c0=='C' )
|
|
{
|
|
this->BZR->DoModification(PathConflicting, path);
|
|
return;
|
|
}
|
|
|
|
if ( c0 == '+' || c0 == 'R' || c0 == 'P'
|
|
|| c1=='M' || c1=='K' || c1=='N' || c1=='D'
|
|
|| c2 =='*' )
|
|
{
|
|
this->BZR->DoModification(PathModified, path);
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void cmCTestBZR::LoadModifications()
|
|
{
|
|
// Run "bzr status" which reports local modifications.
|
|
const char* bzr = this->CommandLineTool.c_str();
|
|
const char* bzr_status[] = {bzr, "status", "-SV", 0};
|
|
StatusParser out(this, "status-out> ");
|
|
OutputLogger err(this->Log, "status-err> ");
|
|
this->RunChild(bzr_status, &out, &err);
|
|
}
|