~ubuntu-branches/ubuntu/trusty/cmake3/trusty-updates

« back to all changes in this revision

Viewing changes to Source/CTest/cmCTestSVN.cxx

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2017-02-23 17:55:24 UTC
  • Revision ID: package-import@ubuntu.com-20170223175524-5nh7s4pu97fsa0t7
Tags: upstream-3.5.1
ImportĀ upstreamĀ versionĀ 3.5.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*============================================================================
 
2
  CMake - Cross Platform Makefile Generator
 
3
  Copyright 2000-2009 Kitware, Inc.
 
4
 
 
5
  Distributed under the OSI-approved BSD License (the "License");
 
6
  see accompanying file Copyright.txt for details.
 
7
 
 
8
  This software is distributed WITHOUT ANY WARRANTY; without even the
 
9
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
10
  See the License for more information.
 
11
============================================================================*/
 
12
#include "cmCTestSVN.h"
 
13
 
 
14
#include "cmCTest.h"
 
15
#include "cmSystemTools.h"
 
16
#include "cmXMLParser.h"
 
17
#include "cmXMLWriter.h"
 
18
 
 
19
#include <cmsys/RegularExpression.hxx>
 
20
 
 
21
struct cmCTestSVN::Revision: public cmCTestVC::Revision
 
22
{
 
23
  cmCTestSVN::SVNInfo* SVNInfo;
 
24
};
 
25
 
 
26
//----------------------------------------------------------------------------
 
27
cmCTestSVN::cmCTestSVN(cmCTest* ct, std::ostream& log):
 
28
  cmCTestGlobalVC(ct, log)
 
29
{
 
30
  this->PriorRev = this->Unknown;
 
31
}
 
32
 
 
33
//----------------------------------------------------------------------------
 
34
cmCTestSVN::~cmCTestSVN()
 
35
{
 
36
}
 
37
 
 
38
//----------------------------------------------------------------------------
 
39
void cmCTestSVN::CleanupImpl()
 
40
{
 
41
  std::vector<const char*> svn_cleanup;
 
42
  svn_cleanup.push_back("cleanup");
 
43
  OutputLogger out(this->Log, "cleanup-out> ");
 
44
  OutputLogger err(this->Log, "cleanup-err> ");
 
45
  this->RunSVNCommand(svn_cleanup, &out, &err);
 
46
}
 
47
 
 
48
//----------------------------------------------------------------------------
 
49
class cmCTestSVN::InfoParser: public cmCTestVC::LineParser
 
50
{
 
51
public:
 
52
  InfoParser(cmCTestSVN* svn,
 
53
             const char* prefix,
 
54
             std::string& rev,
 
55
             SVNInfo& svninfo):
 
56
      Rev(rev), SVNRepo(svninfo)
 
57
    {
 
58
    this->SetLog(&svn->Log, prefix);
 
59
    this->RegexRev.compile("^Revision: ([0-9]+)");
 
60
    this->RegexURL.compile("^URL: +([^ ]+) *$");
 
61
    this->RegexRoot.compile("^Repository Root: +([^ ]+) *$");
 
62
    }
 
63
private:
 
64
  std::string& Rev;
 
65
  cmCTestSVN::SVNInfo& SVNRepo;
 
66
  cmsys::RegularExpression RegexRev;
 
67
  cmsys::RegularExpression RegexURL;
 
68
  cmsys::RegularExpression RegexRoot;
 
69
  virtual bool ProcessLine()
 
70
    {
 
71
    if(this->RegexRev.find(this->Line))
 
72
      {
 
73
      this->Rev = this->RegexRev.match(1);
 
74
      }
 
75
    else if(this->RegexURL.find(this->Line))
 
76
      {
 
77
      this->SVNRepo.URL = this->RegexURL.match(1);
 
78
      }
 
79
    else if(this->RegexRoot.find(this->Line))
 
80
      {
 
81
      this->SVNRepo.Root = this->RegexRoot.match(1);
 
82
      }
 
83
    return true;
 
84
    }
 
85
};
 
86
 
 
87
//----------------------------------------------------------------------------
 
88
static bool cmCTestSVNPathStarts(std::string const& p1, std::string const& p2)
 
89
{
 
90
  // Does path p1 start with path p2?
 
91
  if(p1.size() == p2.size())
 
92
    {
 
93
    return p1 == p2;
 
94
    }
 
95
  else if(p1.size() > p2.size() && p1[p2.size()] == '/')
 
96
    {
 
97
    return strncmp(p1.c_str(), p2.c_str(), p2.size()) == 0;
 
98
    }
 
99
  else
 
100
    {
 
101
    return false;
 
102
    }
 
103
}
 
104
 
 
105
//----------------------------------------------------------------------------
 
106
std::string cmCTestSVN::LoadInfo(SVNInfo& svninfo)
 
107
{
 
108
  // Run "svn info" to get the repository info from the work tree.
 
109
  std::vector<const char*> svn_info;
 
110
  svn_info.push_back("info");
 
111
  svn_info.push_back(svninfo.LocalPath.c_str());
 
112
  std::string rev;
 
113
  InfoParser out(this, "info-out> ", rev, svninfo);
 
114
  OutputLogger err(this->Log, "info-err> ");
 
115
  this->RunSVNCommand(svn_info, &out, &err);
 
116
  return rev;
 
117
}
 
118
 
 
119
//----------------------------------------------------------------------------
 
120
void cmCTestSVN::NoteOldRevision()
 
121
{
 
122
  // Info for root repository
 
123
  this->Repositories.push_back( SVNInfo("") );
 
124
  this->RootInfo = &(this->Repositories.back());
 
125
  // Info for the external repositories
 
126
  this->LoadExternals();
 
127
 
 
128
  // Get info for all the repositories
 
129
  std::list<SVNInfo>::iterator itbeg = this->Repositories.begin();
 
130
  std::list<SVNInfo>::iterator itend = this->Repositories.end();
 
131
  for( ; itbeg != itend ; itbeg++)
 
132
    {
 
133
    SVNInfo& svninfo = *itbeg;
 
134
    svninfo.OldRevision = this->LoadInfo(svninfo);
 
135
    this->Log << "Revision for repository '" << svninfo.LocalPath
 
136
              << "' before update: " << svninfo.OldRevision << "\n";
 
137
    cmCTestLog(this->CTest, HANDLER_OUTPUT,
 
138
               "   Old revision of external repository '"
 
139
               << svninfo.LocalPath << "' is: "
 
140
               << svninfo.OldRevision << "\n");
 
141
    }
 
142
 
 
143
  // Set the global old revision to the one of the root
 
144
  this->OldRevision = this->RootInfo->OldRevision;
 
145
  this->PriorRev.Rev = this->OldRevision;
 
146
}
 
147
 
 
148
//----------------------------------------------------------------------------
 
149
void cmCTestSVN::NoteNewRevision()
 
150
{
 
151
  // Get info for the external repositories
 
152
  std::list<SVNInfo>::iterator itbeg = this->Repositories.begin();
 
153
  std::list<SVNInfo>::iterator itend = this->Repositories.end();
 
154
  for( ; itbeg != itend ; itbeg++)
 
155
    {
 
156
    SVNInfo& svninfo = *itbeg;
 
157
    svninfo.NewRevision = this->LoadInfo(svninfo);
 
158
    this->Log << "Revision for repository '" << svninfo.LocalPath
 
159
              << "' after update: " << svninfo.NewRevision << "\n";
 
160
    cmCTestLog(this->CTest, HANDLER_OUTPUT,
 
161
               "   New revision of external repository '"
 
162
               << svninfo.LocalPath << "' is: "
 
163
               << svninfo.NewRevision << "\n");
 
164
 
 
165
    // svninfo.Root = ""; // uncomment to test GuessBase
 
166
    this->Log << "Repository '" << svninfo.LocalPath
 
167
              << "' URL = " << svninfo.URL << "\n";
 
168
    this->Log << "Repository '" << svninfo.LocalPath
 
169
              << "' Root = " << svninfo.Root << "\n";
 
170
 
 
171
    // Compute the base path the working tree has checked out under
 
172
    // the repository root.
 
173
    if(!svninfo.Root.empty()
 
174
       && cmCTestSVNPathStarts(svninfo.URL, svninfo.Root))
 
175
      {
 
176
      svninfo.Base = cmCTest::DecodeURL(
 
177
            svninfo.URL.substr(svninfo.Root.size()));
 
178
      svninfo.Base += "/";
 
179
      }
 
180
    this->Log << "Repository '" << svninfo.LocalPath
 
181
              << "' Base = " << svninfo.Base << "\n";
 
182
 
 
183
  }
 
184
 
 
185
  // Set the global new revision to the one of the root
 
186
  this->NewRevision = this->RootInfo->NewRevision;
 
187
}
 
188
 
 
189
//----------------------------------------------------------------------------
 
190
void cmCTestSVN::GuessBase(SVNInfo& svninfo,
 
191
                           std::vector<Change> const& changes)
 
192
{
 
193
  // Subversion did not give us a good repository root so we need to
 
194
  // guess the base path from the URL and the paths in a revision with
 
195
  // changes under it.
 
196
 
 
197
  // Consider each possible URL suffix from longest to shortest.
 
198
  for(std::string::size_type slash = svninfo.URL.find('/');
 
199
      svninfo.Base.empty() && slash != std::string::npos;
 
200
      slash = svninfo.URL.find('/', slash+1))
 
201
    {
 
202
    // If the URL suffix is a prefix of at least one path then it is the base.
 
203
    std::string base = cmCTest::DecodeURL(svninfo.URL.substr(slash));
 
204
    for(std::vector<Change>::const_iterator ci = changes.begin();
 
205
        svninfo.Base.empty() && ci != changes.end(); ++ci)
 
206
      {
 
207
      if(cmCTestSVNPathStarts(ci->Path, base))
 
208
        {
 
209
        svninfo.Base = base;
 
210
        }
 
211
      }
 
212
    }
 
213
 
 
214
  // We always append a slash so that we know paths beginning in the
 
215
  // base lie under its path.  If no base was found then the working
 
216
  // tree must be a checkout of the entire repo and this will match
 
217
  // the leading slash in all paths.
 
218
  svninfo.Base += "/";
 
219
 
 
220
  this->Log << "Guessed Base = " << svninfo.Base << "\n";
 
221
}
 
222
 
 
223
//----------------------------------------------------------------------------
 
224
class cmCTestSVN::UpdateParser: public cmCTestVC::LineParser
 
225
{
 
226
public:
 
227
  UpdateParser(cmCTestSVN* svn, const char* prefix): SVN(svn)
 
228
    {
 
229
    this->SetLog(&svn->Log, prefix);
 
230
    this->RegexUpdate.compile("^([ADUCGE ])([ADUCGE ])[B ] +(.+)$");
 
231
    }
 
232
private:
 
233
  cmCTestSVN* SVN;
 
234
  cmsys::RegularExpression RegexUpdate;
 
235
 
 
236
  bool ProcessLine()
 
237
    {
 
238
    if(this->RegexUpdate.find(this->Line))
 
239
      {
 
240
      this->DoPath(this->RegexUpdate.match(1)[0],
 
241
                   this->RegexUpdate.match(2)[0],
 
242
                   this->RegexUpdate.match(3));
 
243
      }
 
244
    return true;
 
245
    }
 
246
 
 
247
  void DoPath(char path_status, char prop_status, std::string const& path)
 
248
    {
 
249
    char status = (path_status != ' ')? path_status : prop_status;
 
250
    std::string dir = cmSystemTools::GetFilenamePath(path);
 
251
    std::string name = cmSystemTools::GetFilenameName(path);
 
252
    // See "svn help update".
 
253
    switch(status)
 
254
      {
 
255
      case 'G':
 
256
        this->SVN->Dirs[dir][name].Status = PathModified;
 
257
        break;
 
258
      case 'C':
 
259
        this->SVN->Dirs[dir][name].Status = PathConflicting;
 
260
        break;
 
261
      case 'A': case 'D': case 'U':
 
262
        this->SVN->Dirs[dir][name].Status = PathUpdated;
 
263
        break;
 
264
      case 'E': // TODO?
 
265
      case '?': case ' ': default:
 
266
        break;
 
267
      }
 
268
    }
 
269
};
 
270
 
 
271
//----------------------------------------------------------------------------
 
272
bool cmCTestSVN::UpdateImpl()
 
273
{
 
274
  // Get user-specified update options.
 
275
  std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
 
276
  if(opts.empty())
 
277
    {
 
278
    opts = this->CTest->GetCTestConfiguration("SVNUpdateOptions");
 
279
    }
 
280
  std::vector<std::string> args = cmSystemTools::ParseArguments(opts.c_str());
 
281
 
 
282
  // Specify the start time for nightly testing.
 
283
  if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
 
284
    {
 
285
    args.push_back("-r{" + this->GetNightlyTime() + " +0000}");
 
286
    }
 
287
 
 
288
  std::vector<char const*> svn_update;
 
289
  svn_update.push_back("update");
 
290
  for(std::vector<std::string>::const_iterator ai = args.begin();
 
291
      ai != args.end(); ++ai)
 
292
    {
 
293
    svn_update.push_back(ai->c_str());
 
294
    }
 
295
 
 
296
  UpdateParser out(this, "up-out> ");
 
297
  OutputLogger err(this->Log, "up-err> ");
 
298
  return this->RunSVNCommand(svn_update, &out, &err);
 
299
}
 
300
 
 
301
//----------------------------------------------------------------------------
 
302
bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters,
 
303
    OutputParser* out, OutputParser* err)
 
304
{
 
305
  if(parameters.empty()) return false;
 
306
 
 
307
  std::vector<char const*> args;
 
308
  args.push_back(this->CommandLineTool.c_str());
 
309
 
 
310
  args.insert(args.end(), parameters.begin(), parameters.end());
 
311
 
 
312
  args.push_back("--non-interactive");
 
313
 
 
314
  std::string userOptions =
 
315
    this->CTest->GetCTestConfiguration("SVNOptions");
 
316
 
 
317
  std::vector<std::string> parsedUserOptions =
 
318
    cmSystemTools::ParseArguments(userOptions.c_str());
 
319
  for(std::vector<std::string>::iterator i = parsedUserOptions.begin();
 
320
      i != parsedUserOptions.end(); ++i)
 
321
    {
 
322
    args.push_back(i->c_str());
 
323
    }
 
324
 
 
325
  args.push_back(0);
 
326
 
 
327
  if(strcmp(parameters[0], "update") == 0)
 
328
    {
 
329
    return RunUpdateCommand(&args[0], out, err);
 
330
    }
 
331
  else
 
332
    {
 
333
    return RunChild(&args[0], out, err);
 
334
    }
 
335
}
 
336
 
 
337
//----------------------------------------------------------------------------
 
338
class cmCTestSVN::LogParser: public cmCTestVC::OutputLogger,
 
339
                             private cmXMLParser
 
340
{
 
341
public:
 
342
  LogParser(cmCTestSVN* svn, const char* prefix, SVNInfo& svninfo):
 
343
    OutputLogger(svn->Log, prefix), SVN(svn), SVNRepo(svninfo)
 
344
  { this->InitializeParser(); }
 
345
  ~LogParser() { this->CleanupParser(); }
 
346
private:
 
347
  cmCTestSVN* SVN;
 
348
  cmCTestSVN::SVNInfo& SVNRepo;
 
349
 
 
350
  typedef cmCTestSVN::Revision Revision;
 
351
  typedef cmCTestSVN::Change Change;
 
352
  Revision Rev;
 
353
  std::vector<Change> Changes;
 
354
  Change CurChange;
 
355
  std::vector<char> CData;
 
356
 
 
357
  virtual bool ProcessChunk(const char* data, int length)
 
358
    {
 
359
    this->OutputLogger::ProcessChunk(data, length);
 
360
    this->ParseChunk(data, length);
 
361
    return true;
 
362
    }
 
363
 
 
364
  virtual void StartElement(const std::string& name, const char** atts)
 
365
    {
 
366
    this->CData.clear();
 
367
    if(name == "logentry")
 
368
      {
 
369
      this->Rev = Revision();
 
370
      this->Rev.SVNInfo = &SVNRepo;
 
371
      if(const char* rev = this->FindAttribute(atts, "revision"))
 
372
        {
 
373
        this->Rev.Rev = rev;
 
374
        }
 
375
      this->Changes.clear();
 
376
      }
 
377
    else if(name == "path")
 
378
      {
 
379
      this->CurChange = Change();
 
380
      if(const char* action = this->FindAttribute(atts, "action"))
 
381
        {
 
382
        this->CurChange.Action = action[0];
 
383
        }
 
384
      }
 
385
    }
 
386
 
 
387
  virtual void CharacterDataHandler(const char* data, int length)
 
388
    {
 
389
    this->CData.insert(this->CData.end(), data, data+length);
 
390
    }
 
391
 
 
392
  virtual void EndElement(const std::string& name)
 
393
    {
 
394
    if(name == "logentry")
 
395
      {
 
396
      this->SVN->DoRevisionSVN(this->Rev, this->Changes);
 
397
      }
 
398
    else if(!this->CData.empty() && name == "path")
 
399
      {
 
400
      std::string orig_path(&this->CData[0], this->CData.size());
 
401
      std::string new_path = SVNRepo.BuildLocalPath( orig_path );
 
402
      this->CurChange.Path.assign(new_path);
 
403
      this->Changes.push_back(this->CurChange);
 
404
      }
 
405
    else if(!this->CData.empty() && name == "author")
 
406
      {
 
407
      this->Rev.Author.assign(&this->CData[0], this->CData.size());
 
408
      }
 
409
    else if(!this->CData.empty() && name == "date")
 
410
      {
 
411
      this->Rev.Date.assign(&this->CData[0], this->CData.size());
 
412
      }
 
413
    else if(!this->CData.empty() && name == "msg")
 
414
      {
 
415
      this->Rev.Log.assign(&this->CData[0], this->CData.size());
 
416
      }
 
417
    this->CData.clear();
 
418
    }
 
419
 
 
420
  virtual void ReportError(int, int, const char* msg)
 
421
    {
 
422
    this->SVN->Log << "Error parsing svn log xml: " << msg << "\n";
 
423
    }
 
424
};
 
425
 
 
426
//----------------------------------------------------------------------------
 
427
void cmCTestSVN::LoadRevisions()
 
428
{
 
429
  // Get revisions for all the external repositories
 
430
  std::list<SVNInfo>::iterator itbeg = this->Repositories.begin();
 
431
  std::list<SVNInfo>::iterator itend = this->Repositories.end();
 
432
  for( ; itbeg != itend ; itbeg++)
 
433
    {
 
434
    SVNInfo& svninfo = *itbeg;
 
435
    LoadRevisions(svninfo);
 
436
    }
 
437
}
 
438
 
 
439
//----------------------------------------------------------------------------
 
440
void cmCTestSVN::LoadRevisions(SVNInfo &svninfo)
 
441
{
 
442
  // We are interested in every revision included in the update.
 
443
  std::string revs;
 
444
  if(atoi(svninfo.OldRevision.c_str()) < atoi(svninfo.NewRevision.c_str()))
 
445
    {
 
446
    revs = "-r" + svninfo.OldRevision + ":" + svninfo.NewRevision;
 
447
    }
 
448
  else
 
449
    {
 
450
    revs = "-r" + svninfo.NewRevision;
 
451
    }
 
452
 
 
453
  // Run "svn log" to get all global revisions of interest.
 
454
  std::vector<const char*> svn_log;
 
455
  svn_log.push_back("log");
 
456
  svn_log.push_back("--xml");
 
457
  svn_log.push_back("-v");
 
458
  svn_log.push_back(revs.c_str());
 
459
  svn_log.push_back(svninfo.LocalPath.c_str());
 
460
  LogParser out(this, "log-out> ", svninfo);
 
461
  OutputLogger err(this->Log, "log-err> ");
 
462
  this->RunSVNCommand(svn_log, &out, &err);
 
463
}
 
464
 
 
465
//----------------------------------------------------------------------------
 
466
void cmCTestSVN::DoRevisionSVN(Revision const& revision,
 
467
                               std::vector<Change> const& changes)
 
468
{
 
469
  // Guess the base checkout path from the changes if necessary.
 
470
  if(this->RootInfo->Base.empty() && !changes.empty())
 
471
    {
 
472
    this->GuessBase(*this->RootInfo, changes);
 
473
    }
 
474
 
 
475
  // Ignore changes in the old revision for external repositories
 
476
  if(revision.Rev == revision.SVNInfo->OldRevision
 
477
     && revision.SVNInfo->LocalPath != "")
 
478
    {
 
479
    return;
 
480
    }
 
481
 
 
482
  this->cmCTestGlobalVC::DoRevision(revision, changes);
 
483
}
 
484
 
 
485
//----------------------------------------------------------------------------
 
486
class cmCTestSVN::StatusParser: public cmCTestVC::LineParser
 
487
{
 
488
public:
 
489
  StatusParser(cmCTestSVN* svn, const char* prefix): SVN(svn)
 
490
    {
 
491
    this->SetLog(&svn->Log, prefix);
 
492
    this->RegexStatus.compile("^([ACDIMRX?!~ ])([CM ])[ L]... +(.+)$");
 
493
    }
 
494
private:
 
495
  cmCTestSVN* SVN;
 
496
  cmsys::RegularExpression RegexStatus;
 
497
  bool ProcessLine()
 
498
    {
 
499
    if(this->RegexStatus.find(this->Line))
 
500
      {
 
501
      this->DoPath(this->RegexStatus.match(1)[0],
 
502
                   this->RegexStatus.match(2)[0],
 
503
                   this->RegexStatus.match(3));
 
504
      }
 
505
    return true;
 
506
    }
 
507
 
 
508
  void DoPath(char path_status, char prop_status, std::string const& path)
 
509
    {
 
510
    char status = (path_status != ' ')? path_status : prop_status;
 
511
    // See "svn help status".
 
512
    switch(status)
 
513
      {
 
514
      case 'M': case '!': case 'A': case 'D': case 'R':
 
515
        this->SVN->DoModification(PathModified, path);
 
516
        break;
 
517
      case 'C': case '~':
 
518
        this->SVN->DoModification(PathConflicting, path);
 
519
        break;
 
520
      case 'X': case 'I': case '?': case ' ': default:
 
521
        break;
 
522
      }
 
523
    }
 
524
};
 
525
 
 
526
//----------------------------------------------------------------------------
 
527
void cmCTestSVN::LoadModifications()
 
528
{
 
529
  // Run "svn status" which reports local modifications.
 
530
  std::vector<const char*> svn_status;
 
531
  svn_status.push_back("status");
 
532
  StatusParser out(this, "status-out> ");
 
533
  OutputLogger err(this->Log, "status-err> ");
 
534
  this->RunSVNCommand(svn_status, &out, &err);
 
535
}
 
536
 
 
537
//----------------------------------------------------------------------------
 
538
void cmCTestSVN::WriteXMLGlobal(cmXMLWriter& xml)
 
539
{
 
540
  this->cmCTestGlobalVC::WriteXMLGlobal(xml);
 
541
 
 
542
  xml.Element("SVNPath", this->RootInfo->Base);
 
543
}
 
544
 
 
545
//----------------------------------------------------------------------------
 
546
class cmCTestSVN::ExternalParser: public cmCTestVC::LineParser
 
547
{
 
548
public:
 
549
  ExternalParser(cmCTestSVN* svn, const char* prefix): SVN(svn)
 
550
    {
 
551
    this->SetLog(&svn->Log, prefix);
 
552
    this->RegexExternal.compile("^X..... +(.+)$");
 
553
    }
 
554
private:
 
555
  cmCTestSVN* SVN;
 
556
  cmsys::RegularExpression RegexExternal;
 
557
  bool ProcessLine()
 
558
    {
 
559
    if(this->RegexExternal.find(this->Line))
 
560
      {
 
561
      this->DoPath(this->RegexExternal.match(1));
 
562
      }
 
563
    return true;
 
564
    }
 
565
 
 
566
  void DoPath(std::string const& path)
 
567
    {
 
568
    // Get local path relative to the source directory
 
569
    std::string local_path;
 
570
    if(path.size() > this->SVN->SourceDirectory.size() &&
 
571
       strncmp(path.c_str(), this->SVN->SourceDirectory.c_str(),
 
572
               this->SVN->SourceDirectory.size()) == 0)
 
573
      {
 
574
      local_path = path.c_str() + this->SVN->SourceDirectory.size() + 1;
 
575
      }
 
576
    else
 
577
      {
 
578
      local_path = path;
 
579
      }
 
580
    this->SVN->Repositories.push_back( SVNInfo(local_path.c_str()) );
 
581
    }
 
582
};
 
583
 
 
584
//----------------------------------------------------------------------------
 
585
void cmCTestSVN::LoadExternals()
 
586
{
 
587
  // Run "svn status" to get the list of external repositories
 
588
  std::vector<const char*> svn_status;
 
589
  svn_status.push_back("status");
 
590
  ExternalParser out(this, "external-out> ");
 
591
  OutputLogger err(this->Log, "external-err> ");
 
592
  this->RunSVNCommand(svn_status, &out, &err);
 
593
}
 
594
 
 
595
//----------------------------------------------------------------------------
 
596
std::string cmCTestSVN::SVNInfo::BuildLocalPath(std::string const& path) const
 
597
{
 
598
  std::string local_path;
 
599
 
 
600
  // Add local path prefix if not empty
 
601
  if (!this->LocalPath.empty())
 
602
    {
 
603
    local_path += this->LocalPath;
 
604
    local_path += "/";
 
605
    }
 
606
 
 
607
  // Add path with base prefix removed
 
608
  if(path.size() > this->Base.size() &&
 
609
     strncmp(path.c_str(), this->Base.c_str(), this->Base.size()) == 0)
 
610
    {
 
611
    local_path += (path.c_str() + this->Base.size());
 
612
    }
 
613
  else
 
614
    {
 
615
    local_path += path;
 
616
  }
 
617
 
 
618
  return local_path;
 
619
}