cmOutputConverter.cxx 20.9 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 START_OUTPUT:
Stephen Kelly's avatar
Stephen Kelly committed
56
      result = this->StateSnapshot.GetDirectory().GetCurrentBinary();
57 58
      break;
  }
Stephen Kelly's avatar
Stephen Kelly committed
59 60

  return this->ConvertToRelativePath(result, source);
61 62 63 64 65 66 67 68
}

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);
69 70 71 72
  return this->ConvertToOutputFormat(result, output);
}

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

86
std::string cmOutputConverter::ConvertDirectorySeparatorsForShell(
87
  const std::string& source) const
88 89 90 91 92
{
  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.
93 94
  if (this->GetState()->UseMSYSShell() && !this->LinkScriptShell) {
    if (result.size() > 2 && result[1] == ':') {
95 96 97
      result[1] = result[0];
      result[0] = '/';
    }
98 99
  }
  if (this->GetState()->UseWindowsShell()) {
100
    std::replace(result.begin(), result.end(), '/', '\\');
101
  }
102 103 104
  return result;
}

105 106 107 108 109 110
static bool cmOutputConverterNotAbove(const char* a, const char* b)
{
  return (cmSystemTools::ComparePath(a, b) ||
          cmSystemTools::IsSubDirectory(a, b));
}

111 112 113
std::string cmOutputConverter::ConvertToRelativePath(
  const std::vector<std::string>& local, const std::string& in_remote,
  bool force) const
114
{
115 116 117 118 119 120 121 122 123 124 125
  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] != '\"');
126 127

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

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

135 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
  // 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;
168
  }
169 170 171

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

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

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

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

  // 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.
206
  for (unsigned int i = common; i < local.size(); ++i) {
207
    relative += "..";
208
    if (i < local.size() - 1) {
209 210
      relative += "/";
    }
211
  }
212 213 214 215 216 217 218 219

  // 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.

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

  // Finally return the path.
  return relative;
}

static bool cmOutputConverterIsShellOperator(const std::string& str)
{
  static std::set<std::string> shellOperators;
232
  if (shellOperators.empty()) {
233 234 235 236 237 238 239 240 241 242 243 244
    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");
245
  }
246 247 248 249
  return shellOperators.count(str) > 0;
}

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

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

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

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

312 313
std::string cmOutputConverter::EscapeWindowsShellArgument(const char* arg,
                                                          int shell_flags)
314
{
315
  return Shell_GetArgumentForWindows(arg, shell_flags);
316 317
}

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

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

cmState* cmOutputConverter::GetState() const
{
  return this->StateSnapshot.GetState();
}
347 348 349 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

/*

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 == '#') ||
390 391 392
          (c == '&') || (c == '$') || (c == '(') || (c == ')') || (c == '~') ||
          (c == '<') || (c == '>') || (c == '|') || (c == '*') || (c == '^') ||
          (c == '\\'));
393 394 395 396
}

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

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

  /* On all platforms quotes are needed to preserve whitespace.  */
409
  if (Shell__CharIsWhitespace(c)) {
410
    return 1;
411
  }
412

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

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

const char* cmOutputConverter::Shell__SkipMakeVariables(const char* c)
{
434 435 436
  while (*c == '$' && *(c + 1) == '(') {
    const char* skip = c + 2;
    while (Shell__CharIsMakeVariableName(*skip)) {
437
      ++skip;
438 439 440 441
    }
    if (*skip == ')') {
      c = skip + 1;
    } else {
442 443
      break;
    }
444
  }
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
  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

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

  /* Scan the string for characters that require quoting.  */
  {
481 482 483 484
    const char* c;
    for (c = in; *c; ++c) {
      /* Look for $(MAKEVAR) syntax if requested.  */
      if (flags & Shell_Flag_AllowMakeVariables) {
485
#if KWSYS_SYSTEM_SHELL_QUOTE_MAKE_VARIABLES
486 487 488 489 490
        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;
491 492
        }
#else
493 494
        /* Skip over the make variable references if any are present.  */
        c = Shell__SkipMakeVariables(c);
495

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

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

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

  return 0;
}

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

  /* 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);
534
  if (needQuotes) {
535
    /* Add the opening quote for this argument.  */
536 537
    if (flags & Shell_Flag_WatcomQuote) {
      if (isUnix) {
538
        out << '"';
539
      }
540
      out << '\'';
541
    } else {
542
      out << '"';
543
    }
544
  }
545 546

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

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

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

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

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

    /* Check whether this character needs escaping for a make tool.  */
601 602
    if (*c == '$') {
      if (flags & Shell_Flag_Make) {
603 604
        /* In Makefiles a dollar is written $$.  The make tool will
           replace it with just $ before passing it to the shell.  */
605
        out << "$$";
606
      } else if (flags & Shell_Flag_VSIDE) {
607 608 609 610 611 612
        /* 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.  */
613
        out << "\"$\"";
614
      } else {
615
        /* Otherwise a dollar is written just $. */
616
        out << '$';
617
      }
618 619
    } else if (*c == '#') {
      if ((flags & Shell_Flag_Make) && (flags & Shell_Flag_WatcomWMake)) {
620 621 622
        /* In Watcom WMake makefiles a pound is written $#.  The make
           tool will replace it with just # before passing it to the
           shell.  */
623
        out << "$#";
624
      } else {
625
        /* Otherwise a pound is written just #. */
626
        out << '#';
627
      }
628 629 630 631
    } else if (*c == '%') {
      if ((flags & Shell_Flag_VSIDE) ||
          ((flags & Shell_Flag_Make) &&
           ((flags & Shell_Flag_MinGWMake) || (flags & Shell_Flag_NMake)))) {
632
        /* In the VS IDE, NMake, or MinGW make a percent is written %%.  */
633
        out << "%%";
634
      } else {
635
        /* Otherwise a percent is written just %. */
636
        out << '%';
637
      }
638 639
    } else if (*c == ';') {
      if (flags & Shell_Flag_VSIDE) {
640 641 642 643 644
        /* 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.  */
645
        out << "\";\"";
646
      } else {
647
        /* Otherwise a semicolon is written just ;. */
648
        out << ';';
649
      }
650
    } else {
651
      /* Store this character.  */
652
      out << *c;
653
    }
654
  }
655

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

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

674
  return out.str();
675 676
}

677 678
std::string cmOutputConverter::Shell_GetArgumentForWindows(const char* in,
                                                           int flags)
679
{
680
  return Shell__GetArgument(in, 0, flags);
681 682
}

683 684
std::string cmOutputConverter::Shell_GetArgumentForUnix(const char* in,
                                                        int flags)
685
{
686
  return Shell__GetArgument(in, 1, flags);
687
}