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 56
  switch (relative) {
    case HOME_OUTPUT:
      result = this->ConvertToRelativePath(
57
        this->GetState()->GetBinaryDirectory(), source);
58 59 60
      break;
    case START_OUTPUT:
      result = this->ConvertToRelativePath(
61
        this->StateSnapshot.GetDirectory().GetCurrentBinary(), source);
62 63
      break;
  }
64 65 66 67 68 69 70 71 72
  return result;
}

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);
73 74 75 76
  return this->ConvertToOutputFormat(result, output);
}

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

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

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

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

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

  // If the path is already relative then just return the path.
135 136
  if (!cmSystemTools::FileIsFullPath(remote_path.c_str())) {
    return remote_path;
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 171
  // 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;
172
  }
173 174 175

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

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

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

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

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

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

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

  // Finally return the path.
  return relative;
}

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

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

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

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

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

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

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

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

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

/*

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 == '#') ||
394 395 396
          (c == '&') || (c == '$') || (c == '(') || (c == ')') || (c == '~') ||
          (c == '<') || (c == '>') || (c == '|') || (c == '*') || (c == '^') ||
          (c == '\\'));
397 398 399 400
}

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

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

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

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

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

const char* cmOutputConverter::Shell__SkipMakeVariables(const char* c)
{
438 439 440
  while (*c == '$' && *(c + 1) == '(') {
    const char* skip = c + 2;
    while (Shell__CharIsMakeVariableName(*skip)) {
441
      ++skip;
442 443 444 445
    }
    if (*skip == ')') {
      c = skip + 1;
    } else {
446 447
      break;
    }
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 474
  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

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

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

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

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

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

  return 0;
}

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

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

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

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

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

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

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

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

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

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

678
  return out.str();
679 680
}

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

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