cmOutputConverter.cxx 21 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium

  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 "cmOutputConverter.h"

#include "cmAlgorithms.h"
15
#include "cmSystemTools.h"
16

17
#include <algorithm>
18
#include <assert.h>
19 20
#include <ctype.h>
#include <set>
21
#include <sstream>
22 23

cmOutputConverter::cmOutputConverter(cmState::Snapshot snapshot)
24 25
  : StateSnapshot(snapshot)
  , LinkScriptShell(false)
26
{
27
  assert(this->StateSnapshot.IsValid());
28 29
}

30 31
std::string cmOutputConverter::ConvertToOutputForExisting(
  const std::string& remote, OutputFormat format) const
32 33 34 35
{
  // If this is a windows shell, the result has a space, and the path
  // already exists, we can use a short-path to reference it without a
  // space.
36 37
  if (this->GetState()->UseWindowsShell() &&
      remote.find(' ') != std::string::npos &&
38
      cmSystemTools::FileExists(remote.c_str())) {
39
    std::string tmp;
40
    if (cmSystemTools::GetShortPath(remote, tmp)) {
41 42
      return this->ConvertToOutputFormat(tmp, format);
    }
43
  }
44

45 46
  // Otherwise, perform standard conversion.
  return this->ConvertToOutputFormat(remote, format);
47 48
}

49 50
std::string cmOutputConverter::ConvertToRelativePath(
  const std::string& source, RelativeRoot relative) const
51
{
52
  std::string result;
53

54 55
  switch (relative) {
    case HOME_OUTPUT:
Stephen Kelly's avatar
Stephen Kelly committed
56
      result = this->GetState()->GetBinaryDirectory();
57 58
      break;
    case START_OUTPUT:
Stephen Kelly's avatar
Stephen Kelly committed
59
      result = this->StateSnapshot.GetDirectory().GetCurrentBinary();
60 61
      break;
  }
Stephen Kelly's avatar
Stephen Kelly committed
62 63

  return this->ConvertToRelativePath(result, source);
64 65 66 67 68 69 70 71
}

std::string cmOutputConverter::Convert(const std::string& source,
                                       RelativeRoot relative,
                                       OutputFormat output) const
{
  // Convert the path to a relative path.
  std::string result = this->ConvertToRelativePath(source, relative);
72 73 74 75
  return this->ConvertToOutputFormat(result, output);
}

std::string cmOutputConverter::ConvertToOutputFormat(const std::string& source,
76
                                                     OutputFormat output) const
77 78 79
{
  std::string result = source;
  // Convert it to an output path.
80
  if (output == SHELL || output == WATCOMQUOTE) {
81
    result = this->ConvertDirectorySeparatorsForShell(source);
82
    result = this->EscapeForShell(result, true, false, output == WATCOMQUOTE);
83
  } else if (output == RESPONSE) {
84
    result = this->EscapeForShell(result, false, false, false);
85
  }
86 87 88
  return result;
}

89
std::string cmOutputConverter::ConvertDirectorySeparatorsForShell(
90
  const std::string& source) const
91 92 93 94 95
{
  std::string result = source;
  // For the MSYS shell convert drive letters to posix paths, so
  // that c:/some/path becomes /c/some/path.  This is needed to
  // avoid problems with the shell path translation.
96 97
  if (this->GetState()->UseMSYSShell() && !this->LinkScriptShell) {
    if (result.size() > 2 && result[1] == ':') {
98 99 100
      result[1] = result[0];
      result[0] = '/';
    }
101 102
  }
  if (this->GetState()->UseWindowsShell()) {
103
    std::replace(result.begin(), result.end(), '/', '\\');
104
  }
105 106 107
  return result;
}

108 109 110 111 112 113
static bool cmOutputConverterNotAbove(const char* a, const char* b)
{
  return (cmSystemTools::ComparePath(a, b) ||
          cmSystemTools::IsSubDirectory(a, b));
}

114 115 116
std::string cmOutputConverter::ConvertToRelativePath(
  const std::vector<std::string>& local, const std::string& in_remote,
  bool force) const
117
{
118 119 120 121 122 123 124 125 126 127 128
  std::string local_path = cmSystemTools::JoinPath(local);
  return force ? this->ForceToRelativePath(local_path, in_remote)
               : this->ConvertToRelativePath(local_path, in_remote);
}

std::string cmOutputConverter::ConvertToRelativePath(
  std::string const& local_path, std::string const& remote_path) const
{
  // The paths should never be quoted.
  assert(local_path[0] != '\"');
  assert(remote_path[0] != '\"');
129 130

  // The local path should never have a trailing slash.
131
  assert(local_path.empty() || local_path[local_path.size() - 1] != '/');
132 133

  // If the path is already relative then just return the path.
134 135
  if (!cmSystemTools::FileIsFullPath(remote_path.c_str())) {
    return remote_path;
136
  }
137

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
  // Skip conversion if the path and local are not both in the source
  // or both in the binary tree.
  if (!((cmOutputConverterNotAbove(
           local_path.c_str(),
           this->StateSnapshot.GetDirectory().GetRelativePathTopBinary()) &&
         cmOutputConverterNotAbove(
           remote_path.c_str(),
           this->StateSnapshot.GetDirectory().GetRelativePathTopBinary())) ||
        (cmOutputConverterNotAbove(
           local_path.c_str(),
           this->StateSnapshot.GetDirectory().GetRelativePathTopSource()) &&
         cmOutputConverterNotAbove(
           remote_path.c_str(),
           this->StateSnapshot.GetDirectory().GetRelativePathTopSource())))) {
    return remote_path;
  }

  return this->ForceToRelativePath(local_path, remote_path);
}

std::string cmOutputConverter::ForceToRelativePath(
  std::string const& local_path, std::string const& remote_path)
{
  // The paths should never be quoted.
  assert(local_path[0] != '\"');
  assert(remote_path[0] != '\"');

  // The local path should never have a trailing slash.
  assert(local_path.empty() || local_path[local_path.size() - 1] != '/');

  // If the path is already relative then just return the path.
  if (!cmSystemTools::FileIsFullPath(remote_path.c_str())) {
    return remote_path;
171
  }
172 173 174

  // Identify the longest shared path component between the remote
  // path and the local path.
175 176
  std::vector<std::string> local;
  cmSystemTools::SplitPath(local_path, local);
177
  std::vector<std::string> remote;
178
  cmSystemTools::SplitPath(remote_path, remote);
179 180 181
  unsigned int common = 0;
  while (common < remote.size() && common < local.size() &&
         cmSystemTools::ComparePath(remote[common], local[common])) {
182
    ++common;
183
  }
184 185

  // If no part of the path is in common then return the full path.
186
  if (common == 0) {
187
    return remote_path;
188
  }
189 190

  // If the entire path is in common then just return a ".".
191
  if (common == remote.size() && common == local.size()) {
192
    return ".";
193
  }
194 195 196

  // If the entire path is in common except for a trailing slash then
  // just return a "./".
197 198
  if (common + 1 == remote.size() && remote[common].empty() &&
      common == local.size()) {
199
    return "./";
200
  }
201 202 203 204 205 206 207 208

  // Construct the relative path.
  std::string relative;

  // First add enough ../ to get up to the level of the shared portion
  // of the path.  Leave off the trailing slash.  Note that the last
  // component of local will never be empty because local should never
  // have a trailing slash.
209
  for (unsigned int i = common; i < local.size(); ++i) {
210
    relative += "..";
211
    if (i < local.size() - 1) {
212 213
      relative += "/";
    }
214
  }
215 216 217 218 219 220 221 222

  // Now add the portion of the destination path that is not included
  // in the shared portion of the path.  Add a slash the first time
  // only if there was already something in the path.  If there was a
  // trailing slash in the input then the last iteration of the loop
  // will add a slash followed by an empty string which will preserve
  // the trailing slash in the output.

223
  if (!relative.empty() && !remote.empty()) {
224
    relative += "/";
225
  }
226
  relative += cmJoin(cmMakeRange(remote).advance(common), "/");
227 228 229 230 231 232 233 234

  // Finally return the path.
  return relative;
}

static bool cmOutputConverterIsShellOperator(const std::string& str)
{
  static std::set<std::string> shellOperators;
235
  if (shellOperators.empty()) {
236 237 238 239 240 241 242 243 244 245 246 247
    shellOperators.insert("<");
    shellOperators.insert(">");
    shellOperators.insert("<<");
    shellOperators.insert(">>");
    shellOperators.insert("|");
    shellOperators.insert("||");
    shellOperators.insert("&&");
    shellOperators.insert("&>");
    shellOperators.insert("1>");
    shellOperators.insert("2>");
    shellOperators.insert("2>&1");
    shellOperators.insert("1>&2");
248
  }
249 250 251 252
  return shellOperators.count(str) > 0;
}

std::string cmOutputConverter::EscapeForShell(const std::string& str,
253 254
                                              bool makeVars, bool forEcho,
                                              bool useWatcomQuote) const
255 256
{
  // Do not escape shell operators.
257
  if (cmOutputConverterIsShellOperator(str)) {
258
    return str;
259
  }
260 261 262

  // Compute the flags for the target shell environment.
  int flags = 0;
263
  if (this->GetState()->UseWindowsVSIDE()) {
264
    flags |= Shell_Flag_VSIDE;
265
  } else if (!this->LinkScriptShell) {
266
    flags |= Shell_Flag_Make;
267 268
  }
  if (makeVars) {
269
    flags |= Shell_Flag_AllowMakeVariables;
270 271
  }
  if (forEcho) {
272
    flags |= Shell_Flag_EchoWindows;
273 274
  }
  if (useWatcomQuote) {
275
    flags |= Shell_Flag_WatcomQuote;
276 277
  }
  if (this->GetState()->UseWatcomWMake()) {
278
    flags |= Shell_Flag_WatcomWMake;
279 280
  }
  if (this->GetState()->UseMinGWMake()) {
281
    flags |= Shell_Flag_MinGWMake;
282 283
  }
  if (this->GetState()->UseNMake()) {
284
    flags |= Shell_Flag_NMake;
285
  }
286

287 288 289
  return this->GetState()->UseWindowsShell()
    ? Shell_GetArgumentForWindows(str.c_str(), flags)
    : Shell_GetArgumentForUnix(str.c_str(), flags);
290 291 292 293 294 295
}

std::string cmOutputConverter::EscapeForCMake(const std::string& str)
{
  // Always double-quote the argument to take care of most escapes.
  std::string result = "\"";
296 297
  for (const char* c = str.c_str(); *c; ++c) {
    if (*c == '"') {
298 299
      // Escape the double quote to avoid ending the argument.
      result += "\\\"";
300
    } else if (*c == '$') {
301 302
      // Escape the dollar to avoid expanding variables.
      result += "\\$";
303
    } else if (*c == '\\') {
304 305
      // Escape the backslash to avoid other escapes.
      result += "\\\\";
306
    } else {
307 308 309
      // Other characters will be parsed correctly.
      result += *c;
    }
310
  }
311 312 313 314
  result += "\"";
  return result;
}

315 316
std::string cmOutputConverter::EscapeWindowsShellArgument(const char* arg,
                                                          int shell_flags)
317
{
318
  return Shell_GetArgumentForWindows(arg, shell_flags);
319 320
}

321 322
cmOutputConverter::FortranFormat cmOutputConverter::GetFortranFormat(
  const char* value)
323 324
{
  FortranFormat format = FortranFormatNone;
325
  if (value && *value) {
326 327
    std::vector<std::string> fmt;
    cmSystemTools::ExpandListArgument(value, fmt);
328 329 330
    for (std::vector<std::string>::iterator fi = fmt.begin(); fi != fmt.end();
         ++fi) {
      if (*fi == "FIXED") {
331
        format = FortranFormatFixed;
332 333
      }
      if (*fi == "FREE") {
334 335 336
        format = FortranFormatFree;
      }
    }
337
  }
338 339 340 341 342 343 344 345 346 347 348 349
  return format;
}

void cmOutputConverter::SetLinkScriptShell(bool linkScriptShell)
{
  this->LinkScriptShell = linkScriptShell;
}

cmState* cmOutputConverter::GetState() const
{
  return this->StateSnapshot.GetState();
}
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392

/*

Notes:

Make variable replacements open a can of worms.  Sometimes they should
be quoted and sometimes not.  Sometimes their replacement values are
already quoted.

VS variables cause problems.  In order to pass the referenced value
with spaces the reference must be quoted.  If the variable value ends
in a backslash then it will escape the ending quote!  In order to make
the ending backslash appear we need this:

  "$(InputDir)\"

However if there is not a trailing backslash then this will put a
quote in the value so we need:

  "$(InputDir)"

Make variable references are platform specific so we should probably
just NOT quote them and let the listfile author deal with it.

*/

/*
TODO: For windows echo:

To display a pipe (|) or redirection character (< or >) when using the
echo command, use a caret character immediately before the pipe or
redirection character (for example, ^>, ^<, or ^| ). If you need to
use the caret character itself (^), use two in a row (^^).
*/

int cmOutputConverter::Shell__CharIsWhitespace(char c)
{
  return ((c == ' ') || (c == '\t'));
}

int cmOutputConverter::Shell__CharNeedsQuotesOnUnix(char c)
{
  return ((c == '\'') || (c == '`') || (c == ';') || (c == '#') ||
393 394 395
          (c == '&') || (c == '$') || (c == '(') || (c == ')') || (c == '~') ||
          (c == '<') || (c == '>') || (c == '|') || (c == '*') || (c == '^') ||
          (c == '\\'));
396 397 398 399
}

int cmOutputConverter::Shell__CharNeedsQuotesOnWindows(char c)
{
400 401
  return ((c == '\'') || (c == '#') || (c == '&') || (c == '<') ||
          (c == '>') || (c == '|') || (c == '^'));
402 403 404 405 406
}

int cmOutputConverter::Shell__CharNeedsQuotes(char c, int isUnix, int flags)
{
  /* On Windows the built-in command shell echo never needs quotes.  */
407
  if (!isUnix && (flags & Shell_Flag_EchoWindows)) {
408
    return 0;
409
  }
410 411

  /* On all platforms quotes are needed to preserve whitespace.  */
412
  if (Shell__CharIsWhitespace(c)) {
413
    return 1;
414
  }
415

416
  if (isUnix) {
417
    /* On UNIX several special characters need quotes to preserve them.  */
418
    if (Shell__CharNeedsQuotesOnUnix(c)) {
419 420
      return 1;
    }
421
  } else {
422
    /* On Windows several special characters need quotes to preserve them.  */
423
    if (Shell__CharNeedsQuotesOnWindows(c)) {
424 425
      return 1;
    }
426
  }
427 428 429 430 431 432 433 434 435 436
  return 0;
}

int cmOutputConverter::Shell__CharIsMakeVariableName(char c)
{
  return c && (c == '_' || isalpha(((int)c)));
}

const char* cmOutputConverter::Shell__SkipMakeVariables(const char* c)
{
437 438 439
  while (*c == '$' && *(c + 1) == '(') {
    const char* skip = c + 2;
    while (Shell__CharIsMakeVariableName(*skip)) {
440
      ++skip;
441 442 443 444
    }
    if (*skip == ')') {
      c = skip + 1;
    } else {
445 446
      break;
    }
447
  }
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
  return c;
}

/*
Allowing make variable replacements opens a can of worms.  Sometimes
they should be quoted and sometimes not.  Sometimes their replacement
values are already quoted or contain escapes.

Some Visual Studio variables cause problems.  In order to pass the
referenced value with spaces the reference must be quoted.  If the
variable value ends in a backslash then it will escape the ending
quote!  In order to make the ending backslash appear we need this:

  "$(InputDir)\"

However if there is not a trailing backslash then this will put a
quote in the value so we need:

  "$(InputDir)"

This macro decides whether we quote an argument just because it
contains a make variable reference.  This should be replaced with a
flag later when we understand applications of this better.
*/
#define KWSYS_SYSTEM_SHELL_QUOTE_MAKE_VARIABLES 0

474 475
int cmOutputConverter::Shell__ArgumentNeedsQuotes(const char* in, int isUnix,
                                                  int flags)
476 477
{
  /* The empty string needs quotes.  */
478
  if (!*in) {
479
    return 1;
480
  }
481 482 483

  /* Scan the string for characters that require quoting.  */
  {
484 485 486 487
    const char* c;
    for (c = in; *c; ++c) {
      /* Look for $(MAKEVAR) syntax if requested.  */
      if (flags & Shell_Flag_AllowMakeVariables) {
488
#if KWSYS_SYSTEM_SHELL_QUOTE_MAKE_VARIABLES
489 490 491 492 493
        const char* skip = Shell__SkipMakeVariables(c);
        if (skip != c) {
          /* We need to quote make variable references to preserve the
             string with contents substituted in its place.  */
          return 1;
494 495
        }
#else
496 497
        /* Skip over the make variable references if any are present.  */
        c = Shell__SkipMakeVariables(c);
498

499 500 501
        /* Stop if we have reached the end of the string.  */
        if (!*c) {
          break;
502 503 504 505
        }
#endif
      }

506 507 508
      /* Check whether this character needs quotes.  */
      if (Shell__CharNeedsQuotes(*c, isUnix, flags)) {
        return 1;
509 510 511 512 513
      }
    }
  }

  /* On Windows some single character arguments need quotes.  */
514
  if (!isUnix && *in && !*(in + 1)) {
515
    char c = *in;
516
    if ((c == '?') || (c == '&') || (c == '^') || (c == '|') || (c == '#')) {
517 518
      return 1;
    }
519
  }
520 521 522 523

  return 0;
}

524 525
std::string cmOutputConverter::Shell__GetArgument(const char* in, int isUnix,
                                                  int flags)
526
{
527
  std::ostringstream out;
528 529 530 531 532 533 534 535 536

  /* String iterator.  */
  const char* c;

  /* Keep track of how many backslashes have been encountered in a row.  */
  int windows_backslashes = 0;

  /* Whether the argument must be quoted.  */
  int needQuotes = Shell__ArgumentNeedsQuotes(in, isUnix, flags);
537
  if (needQuotes) {
538
    /* Add the opening quote for this argument.  */
539 540
    if (flags & Shell_Flag_WatcomQuote) {
      if (isUnix) {
541
        out << '"';
542
      }
543
      out << '\'';
544
    } else {
545
      out << '"';
546
    }
547
  }
548 549

  /* Scan the string for characters that require escaping or quoting.  */
550
  for (c = in; *c; ++c) {
551
    /* Look for $(MAKEVAR) syntax if requested.  */
552
    if (flags & Shell_Flag_AllowMakeVariables) {
553
      const char* skip = Shell__SkipMakeVariables(c);
554
      if (skip != c) {
555
        /* Copy to the end of the make variable references.  */
556
        while (c != skip) {
557
          out << *c++;
558
        }
559 560 561 562 563 564

        /* The make variable reference eliminates any escaping needed
           for preceding backslashes.  */
        windows_backslashes = 0;

        /* Stop if we have reached the end of the string.  */
565
        if (!*c) {
566 567 568
          break;
        }
      }
569
    }
570 571

    /* Check whether this character needs escaping for the shell.  */
572
    if (isUnix) {
573 574
      /* On Unix a few special characters need escaping even inside a
         quoted argument.  */
575
      if (*c == '\\' || *c == '"' || *c == '`' || *c == '$') {
576
        /* This character needs a backslash to escape it.  */
577
        out << '\\';
578
      }
579
    } else if (flags & Shell_Flag_EchoWindows) {
580
      /* On Windows the built-in command shell echo never needs escaping.  */
581
    } else {
582
      /* On Windows only backslashes and double-quotes need escaping.  */
583
      if (*c == '\\') {
584 585
        /* Found a backslash.  It may need to be escaped later.  */
        ++windows_backslashes;
586
      } else if (*c == '"') {
587 588
        /* Found a double-quote.  Escape all immediately preceding
           backslashes.  */
589
        while (windows_backslashes > 0) {
590
          --windows_backslashes;
591
          out << '\\';
592
        }
593 594

        /* Add the backslash to escape the double-quote.  */
595
        out << '\\';
596
      } else {
597 598 599 600
        /* We encountered a normal character.  This eliminates any
           escaping needed for preceding backslashes.  */
        windows_backslashes = 0;
      }
601
    }
602 603

    /* Check whether this character needs escaping for a make tool.  */
604 605
    if (*c == '$') {
      if (flags & Shell_Flag_Make) {
606 607
        /* In Makefiles a dollar is written $$.  The make tool will
           replace it with just $ before passing it to the shell.  */
608
        out << "$$";
609
      } else if (flags & Shell_Flag_VSIDE) {
610 611 612 613 614 615
        /* In a VS IDE a dollar is written "$".  If this is written in
           an un-quoted argument it starts a quoted segment, inserts
           the $ and ends the segment.  If it is written in a quoted
           argument it ends quoting, inserts the $ and restarts
           quoting.  Either way the $ is isolated from surrounding
           text to avoid looking like a variable reference.  */
616
        out << "\"$\"";
617
      } else {
618
        /* Otherwise a dollar is written just $. */
619
        out << '$';
620
      }
621 622
    } else if (*c == '#') {
      if ((flags & Shell_Flag_Make) && (flags & Shell_Flag_WatcomWMake)) {
623 624 625
        /* In Watcom WMake makefiles a pound is written $#.  The make
           tool will replace it with just # before passing it to the
           shell.  */
626
        out << "$#";
627
      } else {
628
        /* Otherwise a pound is written just #. */
629
        out << '#';
630
      }
631 632 633 634
    } else if (*c == '%') {
      if ((flags & Shell_Flag_VSIDE) ||
          ((flags & Shell_Flag_Make) &&
           ((flags & Shell_Flag_MinGWMake) || (flags & Shell_Flag_NMake)))) {
635
        /* In the VS IDE, NMake, or MinGW make a percent is written %%.  */
636
        out << "%%";
637
      } else {
638
        /* Otherwise a percent is written just %. */
639
        out << '%';
640
      }
641 642
    } else if (*c == ';') {
      if (flags & Shell_Flag_VSIDE) {
643 644 645 646 647
        /* In a VS IDE a semicolon is written ";".  If this is written
           in an un-quoted argument it starts a quoted segment,
           inserts the ; and ends the segment.  If it is written in a
           quoted argument it ends quoting, inserts the ; and restarts
           quoting.  Either way the ; is isolated.  */
648
        out << "\";\"";
649
      } else {
650
        /* Otherwise a semicolon is written just ;. */
651
        out << ';';
652
      }
653
    } else {
654
      /* Store this character.  */
655
      out << *c;
656
    }
657
  }
658

659
  if (needQuotes) {
660
    /* Add enough backslashes to escape any trailing ones.  */
661
    while (windows_backslashes > 0) {
662
      --windows_backslashes;
663
      out << '\\';
664
    }
665 666

    /* Add the closing quote for this argument.  */
667
    if (flags & Shell_Flag_WatcomQuote) {
668
      out << '\'';
669
      if (isUnix) {
670
        out << '"';
671
      }
672
    } else {
673
      out << '"';
674
    }
675
  }
676

677
  return out.str();
678 679
}

680 681
std::string cmOutputConverter::Shell_GetArgumentForWindows(const char* in,
                                                           int flags)
682
{
683
  return Shell__GetArgument(in, 0, flags);
684 685
}

686 687
std::string cmOutputConverter::Shell_GetArgumentForUnix(const char* in,
                                                        int flags)
688
{
689
  return Shell__GetArgument(in, 1, flags);
690
}