cmcmd.cxx 60.7 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmcmd.h"
Brad King's avatar
Brad King committed
4

5
#include "cmAlgorithms.h"
6
#include "cmDuration.h"
7
#include "cmGlobalGenerator.h"
8 9
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
10
#include "cmQtAutoGeneratorMocUic.h"
11
#include "cmQtAutoGeneratorRcc.h"
12
#include "cmStateDirectory.h"
13
#include "cmStateSnapshot.h"
14
#include "cmSystemTools.h"
15
#include "cmUtils.hxx"
16
#include "cmVersion.h"
17
#include "cmake.h"
18 19

#if defined(CMAKE_BUILD_WITH_CMAKE)
20
#include "cmDependsFortran.h" // For -E cmake_copy_f90_mod callback.
21 22
#include "cmServer.h"
#include "cmServerConnection.h"
23 24
#endif

25 26 27
#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(_WIN32)
#include "bindexplib.h"
#endif
28

29
#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(_WIN32) && !defined(__CYGWIN__)
30 31 32
#include "cmVisualStudioWCEPlatformParser.h"
#endif

33 34 35 36
#include "cmsys/Directory.hxx"
#include "cmsys/FStream.hxx"
#include "cmsys/Process.h"
#include "cmsys/Terminal.h"
37 38
#include <algorithm>
#include <iostream>
39
#include <iterator>
40
#include <memory> // IWYU pragma: keep
41 42 43
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
44
#include <string.h>
45 46
#include <time.h>

47 48
class cmConnection;

49 50
int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg,
                              std::vector<std::string>::const_iterator argEnd);
51 52
int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
                             std::vector<std::string>::const_iterator argEnd);
53

54 55
void CMakeCommandUsage(const char* program)
{
56
  std::ostringstream errorStream;
57 58

#ifdef CMAKE_BUILD_WITH_CMAKE
59
  /* clang-format off */
60 61
  errorStream
    << "cmake version " << cmVersion::GetCMakeVersion() << "\n";
62
/* clang-format on */
63
#else
64
  /* clang-format off */
65 66
  errorStream
    << "cmake bootstrap\n";
67
/* clang-format on */
68 69 70
#endif
  // If you add new commands, change here,
  // and in cmakemain.cxx in the options table
71
  /* clang-format off */
72
  errorStream
73
    << "Usage: " << program << " -E <command> [arguments...]\n"
74
    << "Available commands: \n"
75 76
    << "  capabilities              - Report capabilities built into cmake "
       "in JSON format\n"
77
    << "  chdir dir cmd [args...]   - run command in a given directory\n"
78
    << "  compare_files file1 file2 - check if file1 is same as file2\n"
79 80
    << "  copy <file>... destination  - copy files to destination "
       "(either file or directory)\n"
81 82
    << "  copy_directory <dir>... destination   - copy content of <dir>... "
       "directories to 'destination' directory\n"
83
    << "  copy_if_different <file>... destination  - copy files if it has "
84
       "changed\n"
85 86
    << "  echo [<string>...]        - displays arguments as text\n"
    << "  echo_append [<string>...] - displays arguments as text but no new "
87
       "line\n"
88 89
    << "  env [--unset=NAME]... [NAME=VALUE]... COMMAND [ARG]...\n"
    << "                            - run command in a modified environment\n"
90
    << "  environment               - display the current environment\n"
91
    << "  make_directory <dir>...   - create parent and <dir> directories\n"
92
    << "  md5sum <file>...          - create MD5 checksum of files\n"
93 94 95 96 97
    << "  sha1sum <file>...         - create SHA1 checksum of files\n"
    << "  sha224sum <file>...       - create SHA224 checksum of files\n"
    << "  sha256sum <file>...       - create SHA256 checksum of files\n"
    << "  sha384sum <file>...       - create SHA384 checksum of files\n"
    << "  sha512sum <file>...       - create SHA512 checksum of files\n"
98
    << "  remove [-f] <file>...     - remove the file(s), use -f to force "
99 100 101 102
       "it\n"
    << "  remove_directory dir      - remove a directory and its contents\n"
    << "  rename oldname newname    - rename a file or directory "
       "(on one volume)\n"
103
    << "  server                    - start cmake in server mode\n"
104
    << "  sleep <number>...         - sleep for given number of seconds\n"
105
    << "  tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n"
106
    << "                            - create or extract a tar or zip archive\n"
107
    << "  time command [args...]    - run command and display elapsed time\n"
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    << "  touch file                - touch a file.\n"
    << "  touch_nocreate file       - touch a file but do not create it.\n"
#if defined(_WIN32) && !defined(__CYGWIN__)
    << "Available on Windows only:\n"
    << "  delete_regv key           - delete registry value\n"
    << "  env_vs8_wince sdkname     - displays a batch file which sets the "
       "environment for the provided Windows CE SDK installed in VS2005\n"
    << "  env_vs9_wince sdkname     - displays a batch file which sets the "
       "environment for the provided Windows CE SDK installed in VS2008\n"
    << "  write_regv key value      - write registry value\n"
#else
    << "Available on UNIX only:\n"
    << "  create_symlink old new    - create a symbolic link new -> old\n"
#endif
    ;
123
  /* clang-format on */
124 125 126 127

  cmSystemTools::Error(errorStream.str().c_str());
}

128 129 130
static bool cmTarFilesFrom(std::string const& file,
                           std::vector<std::string>& files)
{
131
  if (cmSystemTools::FileIsDirectory(file)) {
132 133 134 135
    std::ostringstream e;
    e << "-E tar --files-from= file '" << file << "' is a directory";
    cmSystemTools::Error(e.str().c_str());
    return false;
136
  }
137
  cmsys::ifstream fin(file.c_str());
138
  if (!fin) {
139 140 141 142
    std::ostringstream e;
    e << "-E tar --files-from= file '" << file << "' not found";
    cmSystemTools::Error(e.str().c_str());
    return false;
143
  }
144
  std::string line;
145 146
  while (cmSystemTools::GetLineFromStream(fin, line)) {
    if (line.empty()) {
147
      continue;
148 149
    }
    if (cmHasLiteralPrefix(line, "--add-file=")) {
150
      files.push_back(line.substr(11));
151
    } else if (cmHasLiteralPrefix(line, "-")) {
152 153 154 155 156
      std::ostringstream e;
      e << "-E tar --files-from='" << file << "' file invalid line:\n"
        << line << "\n";
      cmSystemTools::Error(e.str().c_str());
      return false;
157
    } else {
158 159
      files.push_back(line);
    }
160
  }
161 162 163
  return true;
}

164 165
static int HandleIWYU(const std::string& runCmd,
                      const std::string& /* sourceFile */,
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
                      const std::vector<std::string>& orig_cmd)
{
  // Construct the iwyu command line by taking what was given
  // and adding all the arguments we give to the compiler.
  std::vector<std::string> iwyu_cmd;
  cmSystemTools::ExpandListArgument(runCmd, iwyu_cmd, true);
  iwyu_cmd.insert(iwyu_cmd.end(), orig_cmd.begin() + 1, orig_cmd.end());
  // Run the iwyu command line.  Capture its stderr and hide its stdout.
  // Ignore its return code because the tool always returns non-zero.
  std::string stdErr;
  int ret;
  if (!cmSystemTools::RunSingleCommand(iwyu_cmd, nullptr, &stdErr, &ret,
                                       nullptr, cmSystemTools::OUTPUT_NONE)) {
    std::cerr << "Error running '" << iwyu_cmd[0] << "': " << stdErr << "\n";
    return 1;
  }
  // Warn if iwyu reported anything.
  if (stdErr.find("should remove these lines:") != std::string::npos ||
      stdErr.find("should add these lines:") != std::string::npos) {
    std::cerr << "Warning: include-what-you-use reported diagnostics:\n"
              << stdErr << "\n";
  }
  // always return 0 we don't want to break the compile
  return 0;
}

192
static int HandleTidy(const std::string& runCmd, const std::string& sourceFile,
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
                      const std::vector<std::string>& orig_cmd)
{
  // Construct the clang-tidy command line by taking what was given
  // and adding our compiler command line.  The clang-tidy tool will
  // automatically skip over the compiler itself and extract the
  // options.
  int ret;
  std::vector<std::string> tidy_cmd;
  cmSystemTools::ExpandListArgument(runCmd, tidy_cmd, true);
  tidy_cmd.push_back(sourceFile);
  tidy_cmd.push_back("--");
  tidy_cmd.insert(tidy_cmd.end(), orig_cmd.begin(), orig_cmd.end());

  // Run the tidy command line.  Capture its stdout and hide its stderr.
  std::string stdOut;
  std::string stdErr;
  if (!cmSystemTools::RunSingleCommand(tidy_cmd, &stdOut, &stdErr, &ret,
                                       nullptr, cmSystemTools::OUTPUT_NONE)) {
    std::cerr << "Error running '" << tidy_cmd[0] << "': " << stdErr << "\n";
    return 1;
  }
  // Output the stdout from clang-tidy to stderr
  std::cerr << stdOut;
  // If clang-tidy exited with an error do the same.
  if (ret != 0) {
    std::cerr << stdErr;
  }
  return ret;
}

223 224
static int HandleLWYU(const std::string& runCmd,
                      const std::string& /* sourceFile */,
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
                      const std::vector<std::string>&)
{
  // Construct the ldd -r -u (link what you use lwyu) command line
  // ldd -u -r lwuy target
  std::vector<std::string> lwyu_cmd;
  lwyu_cmd.push_back("ldd");
  lwyu_cmd.push_back("-u");
  lwyu_cmd.push_back("-r");
  lwyu_cmd.push_back(runCmd);

  // Run the ldd -u -r command line.
  // Capture its stdout and hide its stderr.
  // Ignore its return code because the tool always returns non-zero
  // if there are any warnings, but we just want to warn.
  std::string stdOut;
  std::string stdErr;
  int ret;
  if (!cmSystemTools::RunSingleCommand(lwyu_cmd, &stdOut, &stdErr, &ret,
                                       nullptr, cmSystemTools::OUTPUT_NONE)) {
    std::cerr << "Error running '" << lwyu_cmd[0] << "': " << stdErr << "\n";
    return 1;
  }

  // Output the stdout from ldd -r -u to stderr
  // Warn if lwyu reported anything.
  if (stdOut.find("Unused direct dependencies:") != std::string::npos) {
    std::cerr << "Warning: " << stdOut;
  }
  return 0;
}

256
static int HandleCppLint(const std::string& runCmd,
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
                         const std::string& sourceFile,
                         const std::vector<std::string>&)
{
  // Construct the cpplint command line.
  std::vector<std::string> cpplint_cmd;
  cmSystemTools::ExpandListArgument(runCmd, cpplint_cmd, true);
  cpplint_cmd.push_back(sourceFile);

  // Run the cpplint command line.  Capture its output.
  std::string stdOut;
  int ret;
  if (!cmSystemTools::RunSingleCommand(cpplint_cmd, &stdOut, &stdOut, &ret,
                                       nullptr, cmSystemTools::OUTPUT_NONE)) {
    std::cerr << "Error running '" << cpplint_cmd[0] << "': " << stdOut
              << "\n";
    return 1;
  }
274
  std::cerr << "Warning: cpplint diagnostics:\n";
275 276
  // Output the output from cpplint to stderr
  std::cerr << stdOut;
277 278 279
  // always return 0 so the build can continue as cpplint returns non-zero
  // for any warning
  return 0;
280 281
}

282
static int HandleCppCheck(const std::string& runCmd,
283 284 285 286 287 288 289
                          const std::string& sourceFile,
                          const std::vector<std::string>& orig_cmd)
{
  // Construct the cpplint command line.
  std::vector<std::string> cppcheck_cmd;
  cmSystemTools::ExpandListArgument(runCmd, cppcheck_cmd, true);
  // extract all the -D, -U, and -I options from the compile line
290
  for (auto const& opt : orig_cmd) {
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
    if (opt.size() > 2) {
      if ((opt[0] == '-') &&
          ((opt[1] == 'D') || (opt[1] == 'I') || (opt[1] == 'U'))) {
        cppcheck_cmd.push_back(opt);
// convert cl / options to - options if needed
#if defined(_WIN32)
      } else if ((opt[0] == '/') &&
                 ((opt[1] == 'D') || (opt[1] == 'I') || (opt[1] == 'U'))) {
        std::string optcopy = opt;
        optcopy[0] = '-';
        cppcheck_cmd.push_back(optcopy);
#endif
      }
    }
  }
  // add the source file
  cppcheck_cmd.push_back(sourceFile);

  // Run the cpplint command line.  Capture its output.
  std::string stdOut;
  std::string stdErr;
  int ret;
  if (!cmSystemTools::RunSingleCommand(cppcheck_cmd, &stdOut, &stdErr, &ret,
                                       nullptr, cmSystemTools::OUTPUT_NONE)) {
    std::cerr << "Error running '" << cppcheck_cmd[0] << "': " << stdOut
              << "\n";
    return 1;
  }
  std::cerr << stdOut;
  // Output the output from cpplint to stderr
  if (stdErr.find("(error)") != std::string::npos ||
      stdErr.find("(warning)") != std::string::npos ||
      stdErr.find("(style)") != std::string::npos ||
      stdErr.find("(performance)") != std::string::npos ||
      stdErr.find("(portability)") != std::string::npos ||
      stdErr.find("(information)") != std::string::npos) {
    std::cerr << "Warning: cppcheck reported diagnostics:\n";
  }
  std::cerr << stdErr;
  // ignore errors so build continues
  return 0;
}

334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
typedef int (*CoCompileHandler)(const std::string&, const std::string&,
                                const std::vector<std::string>&);

struct CoCompiler
{
  const char* Option;
  CoCompileHandler Handler;
  bool NoOriginalCommand;
};

static CoCompiler CoCompilers[] = { // Table of options and handlers.
  { "--cppcheck=", HandleCppCheck, false },
  { "--cpplint=", HandleCppLint, false },
  { "--iwyu=", HandleIWYU, false },
  { "--lwyu=", HandleLWYU, true },
  { "--tidy=", HandleTidy, false }
};

struct CoCompileJob
{
  std::string Command;
  CoCompileHandler Handler;
};

358 359 360
// called when args[0] == "__run_co_compile"
int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args)
{
361 362 363 364 365 366 367
  std::vector<CoCompileJob> jobs;
  std::string sourceFile; // store --source=

  // Default is to run the original command found after -- if the option
  // does not need to do that, it should be specified here, currently only
  // lwyu does that.
  bool runOriginalCmd = true;
368 369 370

  std::vector<std::string> orig_cmd;
  bool doing_options = true;
371 372
  for (std::string::size_type i = 2; i < args.size(); ++i) {
    std::string const& arg = args[i];
373 374 375 376 377 378
    // if the arg is -- then the rest of the args after
    // go into orig_cmd
    if (arg == "--") {
      doing_options = false;
    } else if (doing_options) {
      bool optionFound = false;
379 380
      for (CoCompiler const* cc = cm::cbegin(CoCompilers);
           cc != cm::cend(CoCompilers); ++cc) {
381 382
        size_t optionLen = strlen(cc->Option);
        if (arg.compare(0, optionLen, cc->Option) == 0) {
383
          optionFound = true;
384 385 386 387 388 389 390
          CoCompileJob job;
          job.Command = arg.substr(optionLen);
          job.Handler = cc->Handler;
          jobs.push_back(std::move(job));
          if (cc->NoOriginalCommand) {
            runOriginalCmd = false;
          }
391 392 393 394 395 396
        }
      }
      if (cmHasLiteralPrefix(arg, "--source=")) {
        sourceFile = arg.substr(9);
        optionFound = true;
      }
397
      // if it was not a co-compiler or --source then error
398 399 400 401 402 403 404 405 406
      if (!optionFound) {
        std::cerr << "__run_co_compile given unknown argument: " << arg
                  << "\n";
        return 1;
      }
    } else { // if not doing_options then push to orig_cmd
      orig_cmd.push_back(arg);
    }
  }
407 408 409
  if (jobs.empty()) {
    std::cerr << "__run_co_compile missing command to run. "
                 "Looking for one or more of the following:\n";
410 411
    for (CoCompiler const* cc = cm::cbegin(CoCompilers);
         cc != cm::cend(CoCompilers); ++cc) {
412
      std::cerr << cc->Option << "\n";
413 414 415
    }
    return 1;
  }
416

417 418 419 420 421
  if (runOriginalCmd && orig_cmd.empty()) {
    std::cerr << "__run_co_compile missing compile command after --\n";
    return 1;
  }

422 423 424 425 426 427 428 429 430 431
  for (CoCompileJob const& job : jobs) {
    // call the command handler here
    int ret = job.Handler(job.Command, sourceFile, orig_cmd);

    // if the command returns non-zero then return and fail.
    // for commands that do not want to break the build, they should return
    // 0 no matter what.
    if (ret != 0) {
      return ret;
    }
432
  }
433

434 435
  // if there is no original command to run return now
  if (!runOriginalCmd) {
436
    return 0;
437
  }
438

439
  // Now run the real compiler command and return its result value
440
  int ret;
441 442 443 444 445 446 447 448 449 450
  if (!cmSystemTools::RunSingleCommand(orig_cmd, nullptr, nullptr, &ret,
                                       nullptr,
                                       cmSystemTools::OUTPUT_PASSTHROUGH)) {
    std::cerr << "Error running '" << orig_cmd[0] << "'\n";
    return 1;
  }
  // return the return value from the original compiler command
  return ret;
}

451 452 453
int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
{
  // IF YOU ADD A NEW COMMAND, DOCUMENT IT ABOVE and in cmakemain.cxx
454
  if (args.size() > 1) {
455
    // Copy file
456
    if (args[1] == "copy" && args.size() > 3) {
457 458 459
      // If multiple source files specified,
      // then destination must be directory
      if ((args.size() > 4) &&
460
          (!cmSystemTools::FileIsDirectory(args[args.size() - 1]))) {
461
        std::cerr << "Error: Target (for copy command) \""
462
                  << args[args.size() - 1] << "\" is not a directory.\n";
463
        return 1;
464
      }
465
      // If error occurs we want to continue copying next files.
466
      bool return_value = false;
467 468 469 470 471
      for (std::string::size_type cc = 2; cc < args.size() - 1; cc++) {
        if (!cmSystemTools::cmCopyFile(args[cc].c_str(),
                                       args[args.size() - 1].c_str())) {
          std::cerr << "Error copying file \"" << args[cc] << "\" to \""
                    << args[args.size() - 1] << "\".\n";
472
          return_value = true;
473
        }
474
      }
475 476
      return return_value;
    }
477 478

    // Copy file if different.
479
    if (args[1] == "copy_if_different" && args.size() > 3) {
480 481 482
      // If multiple source files specified,
      // then destination must be directory
      if ((args.size() > 4) &&
483
          (!cmSystemTools::FileIsDirectory(args[args.size() - 1]))) {
484
        std::cerr << "Error: Target (for copy_if_different command) \""
485
                  << args[args.size() - 1] << "\" is not a directory.\n";
486
        return 1;
487
      }
488
      // If error occurs we want to continue copying next files.
489
      bool return_value = false;
490 491 492 493 494
      for (std::string::size_type cc = 2; cc < args.size() - 1; cc++) {
        if (!cmSystemTools::CopyFileIfDifferent(
              args[cc].c_str(), args[args.size() - 1].c_str())) {
          std::cerr << "Error copying file (if different) from \"" << args[cc]
                    << "\" to \"" << args[args.size() - 1] << "\".\n";
495
          return_value = true;
496
        }
497
      }
498 499
      return return_value;
    }
500 501

    // Copy directory content
502
    if (args[1] == "copy_directory" && args.size() > 3) {
503
      // If error occurs we want to continue copying next files.
504
      bool return_value = false;
505
      for (std::string::size_type cc = 2; cc < args.size() - 1; cc++) {
506
        if (!cmSystemTools::CopyADirectory(args[cc], args[args.size() - 1])) {
507 508
          std::cerr << "Error copying directory from \"" << args[cc]
                    << "\" to \"" << args[args.size() - 1] << "\".\n";
509
          return_value = true;
510 511
        }
      }
512 513
      return return_value;
    }
514 515

    // Rename a file or directory
516 517
    if (args[1] == "rename" && args.size() == 4) {
      if (!cmSystemTools::RenameFile(args[2].c_str(), args[3].c_str())) {
518
        std::string e = cmSystemTools::GetLastSystemError();
519 520
        std::cerr << "Error renaming from \"" << args[2] << "\" to \""
                  << args[3] << "\": " << e << "\n";
521 522
        return 1;
      }
523 524
      return 0;
    }
525 526

    // Compare files
527 528 529
    if (args[1] == "compare_files" && args.size() == 4) {
      if (cmSystemTools::FilesDiffer(args[2], args[3])) {
        std::cerr << "Files \"" << args[2] << "\" to \"" << args[3]
530 531 532
                  << "\" are different.\n";
        return 1;
      }
533 534
      return 0;
    }
535

536
#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE)
537 538 539 540
    else if (args[1] == "__create_def") {
      if (args.size() < 4) {
        std::cerr
          << "__create_def Usage: -E __create_def outfile.def objlistfile\n";
541
        return 1;
542
      }
543
      FILE* fout = cmsys::SystemTools::Fopen(args[2].c_str(), "w+");
544
      if (!fout) {
545 546 547
        std::cerr << "could not open output .def file: " << args[2].c_str()
                  << "\n";
        return 1;
548 549 550
      }
      cmsys::ifstream fin(args[3].c_str(), std::ios::in | std::ios::binary);
      if (!fin) {
551 552 553
        std::cerr << "could not open object list file: " << args[3].c_str()
                  << "\n";
        return 1;
554
      }
555
      std::string file;
556
      bindexplib deffile;
557 558 559 560 561 562 563 564 565 566
      while (cmSystemTools::GetLineFromStream(fin, file)) {
        std::string const& ext = cmSystemTools::GetFilenameLastExtension(file);
        if (cmSystemTools::LowerCase(ext) == ".def") {
          if (!deffile.AddDefinitionFile(file.c_str())) {
            return 1;
          }
        } else {
          if (!deffile.AddObjectFile(file.c_str())) {
            return 1;
          }
567
        }
568
      }
569 570
      deffile.WriteFile(fout);
      fclose(fout);
571
      return 0;
572
    }
573
#endif
574 575
    if (args[1] == "__run_co_compile") {
      return cmcmd::HandleCoCompileCommands(args);
576
    }
577

578
    // Echo string
579
    if (args[1] == "echo") {
580
      std::cout << cmJoin(cmMakeRange(args).advance(2), " ") << std::endl;
581
      return 0;
582
    }
583 584

    // Echo string no new line
585
    if (args[1] == "echo_append") {
586
      std::cout << cmJoin(cmMakeRange(args).advance(2), " ");
587
      return 0;
588
    }
589

590
    if (args[1] == "env") {
591 592
      std::vector<std::string>::const_iterator ai = args.begin() + 2;
      std::vector<std::string>::const_iterator ae = args.end();
593
      for (; ai != ae; ++ai) {
594
        std::string const& a = *ai;
595
        if (cmHasLiteralPrefix(a, "--unset=")) {
596 597
          // Unset environment variable.
          cmSystemTools::UnPutEnv(a.c_str() + 8);
598
        } else if (!a.empty() && a[0] == '-') {
599 600 601 602 603
          // Environment variable and command names cannot start in '-',
          // so this must be an unknown option.
          std::cerr << "cmake -E env: unknown option '" << a << "'"
                    << std::endl;
          return 1;
604
        } else if (a.find('=') != std::string::npos) {
605
          // Set environment variable.
606
          cmSystemTools::PutEnv(a);
607
        } else {
608 609 610
          // This is the beginning of the command.
          break;
        }
611
      }
612

613
      if (ai == ae) {
614 615
        std::cerr << "cmake -E env: no command given" << std::endl;
        return 1;
616
      }
617 618 619 620

      // Execute command from remaining arguments.
      std::vector<std::string> cmd(ai, ae);
      int retval;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
621 622
      if (cmSystemTools::RunSingleCommand(cmd, nullptr, nullptr, &retval,
                                          nullptr,
623
                                          cmSystemTools::OUTPUT_PASSTHROUGH)) {
624 625
        return retval;
      }
626 627
      return 1;
    }
628

629
#if defined(CMAKE_BUILD_WITH_CMAKE)
630
    if (args[1] == "environment") {
631 632
      std::vector<std::string> env = cmSystemTools::GetEnvironmentVariables();
      std::vector<std::string>::iterator it;
633
      for (it = env.begin(); it != env.end(); ++it) {
634
        std::cout << *it << std::endl;
635
      }
636 637
      return 0;
    }
638 639
#endif

640
    if (args[1] == "make_directory" && args.size() > 2) {
641
      // If error occurs we want to continue copying next files.
642
      bool return_value = false;
643
      for (std::string::size_type cc = 2; cc < args.size(); cc++) {
644
        if (!cmSystemTools::MakeDirectory(args[cc])) {
645
          std::cerr << "Error creating directory \"" << args[cc] << "\".\n";
646
          return_value = true;
647 648
        }
      }
649 650
      return return_value;
    }
651

652
    if (args[1] == "remove_directory" && args.size() == 3) {
653 654 655
      if (cmSystemTools::FileIsDirectory(args[2]) &&
          !cmSystemTools::RemoveADirectory(args[2])) {
        std::cerr << "Error removing directory \"" << args[2] << "\".\n";
656 657
        return 1;
      }
658 659
      return 0;
    }
660 661

    // Remove file
662
    if (args[1] == "remove" && args.size() > 2) {
663
      bool force = false;
664 665
      for (std::string::size_type cc = 2; cc < args.size(); cc++) {
        if (args[cc] == "\\-f" || args[cc] == "-f") {
666
          force = true;
667
        } else {
668 669
          // Complain if the file could not be removed, still exists,
          // and the -f option was not given.
670
          if (!cmSystemTools::RemoveFile(args[cc]) && !force &&
671
              cmSystemTools::FileExists(args[cc])) {
672 673 674 675
            return 1;
          }
        }
      }
676 677
      return 0;
    }
678

679
    // Touch file
680
    if (args[1] == "touch" && args.size() > 2) {
681 682
      for (std::string::size_type cc = 2; cc < args.size(); cc++) {
        if (!cmSystemTools::Touch(args[cc], true)) {
683 684 685
          return 1;
        }
      }
686 687
      return 0;
    }
688

689
    // Touch file
690
    if (args[1] == "touch_nocreate" && args.size() > 2) {
691
      for (std::string::size_type cc = 2; cc < args.size(); cc++) {
692 693
        // Complain if the file could not be removed, still exists,
        // and the -f option was not given.
694
        if (!cmSystemTools::Touch(args[cc], false)) {
695 696 697
          return 1;
        }
      }
698 699
      return 0;
    }
700

701
    // capabilities
702
    if (args[1] == "capabilities") {
703 704 705 706
      if (args.size() > 2) {
        std::cerr << "-E capabilities accepts no additional arguments\n";
        return 1;
      }
707
      cmake cm(cmake::RoleInternal);
708
#if defined(CMAKE_BUILD_WITH_CMAKE)
709 710 711 712
      std::cout << cm.ReportCapabilities(true);
#else
      std::cout << cm.ReportCapabilities(false);
#endif
713 714
      return 0;
    }
715

Brad King's avatar
Brad King committed
716
    // Sleep command
717
    if (args[1] == "sleep" && args.size() > 2) {
Brad King's avatar
Brad King committed
718
      double total = 0;
719
      for (size_t i = 2; i < args.size(); ++i) {
Brad King's avatar
Brad King committed
720 721 722 723
        double num = 0.0;
        char unit;
        char extra;
        int n = sscanf(args[i].c_str(), "%lg%c%c", &num, &unit, &extra);
724
        if ((n == 1 || (n == 2 && unit == 's')) && num >= 0) {
Brad King's avatar
Brad King committed
725
          total += num;
726
        } else {
Brad King's avatar
Brad King committed
727 728 729 730
          std::cerr << "Unknown sleep time format \"" << args[i] << "\".\n";
          return 1;
        }
      }
731 732 733 734 735
      if (total > 0) {
        cmSystemTools::Delay(static_cast<unsigned int>(total * 1000));
      }
      return 0;
    }
Brad King's avatar
Brad King committed
736

737
    // Clock command
738
    if (args[1] == "time" && args.size() > 2) {
739
      std::vector<std::string> command(args.begin() + 2, args.end());
740 741 742 743 744 745

      clock_t clock_start, clock_finish;
      time_t time_start, time_finish;

      time(&time_start);
      clock_start = clock();
746
      int ret = 0;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
747
      cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret);
748 749 750 751 752 753

      clock_finish = clock();
      time(&time_finish);

      double clocks_per_sec = static_cast<double>(CLOCKS_PER_SEC);
      std::cout << "Elapsed time: "
754 755 756 757 758 759
                << static_cast<long>(time_finish - time_start) << " s. (time)"
                << ", "
                << static_cast<double>(clock_finish - clock_start) /
          clocks_per_sec
                << " s. (clock)"
                << "\n";
760
      return ret;
761
    }
762

763
    // Command to calculate the md5sum of a file
764
    if (args[1] == "md5sum" && args.size() >= 3) {
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
      return HashSumFile(args, cmCryptoHash::AlgoMD5);
    }

    // Command to calculate the sha1sum of a file
    if (args[1] == "sha1sum" && args.size() >= 3) {
      return HashSumFile(args, cmCryptoHash::AlgoSHA1);
    }

    if (args[1] == "sha224sum" && args.size() >= 3) {
      return HashSumFile(args, cmCryptoHash::AlgoSHA224);
    }

    if (args[1] == "sha256sum" && args.size() >= 3) {
      return HashSumFile(args, cmCryptoHash::AlgoSHA256);
    }

    if (args[1] == "sha384sum" && args.size() >= 3) {
      return HashSumFile(args, cmCryptoHash::AlgoSHA384);
    }

    if (args[1] == "sha512sum" && args.size() >= 3) {
      return HashSumFile(args, cmCryptoHash::AlgoSHA512);
787
    }
788 789

    // Command to change directory and run a program.
790
    if (args[1] == "chdir" && args.size() >= 4) {
791
      std::string const& directory = args[2];
792
      if (!cmSystemTools::FileExists(directory)) {
793 794 795
        cmSystemTools::Error("Directory does not exist for chdir command: ",
                             args[2].c_str());
        return 1;
796
      }
797

798 799
      std::string command =
        cmWrap('"', cmMakeRange(args).advance(3), '"', " ");
800
      int retval = 0;
801
      if (cmSystemTools::RunSingleCommand(
Daniel Pfeifer's avatar
Daniel Pfeifer committed
802
            command.c_str(), nullptr, nullptr, &retval, directory.c_str(),
803
            cmSystemTools::OUTPUT_PASSTHROUGH, cmDuration::zero())) {
804
        return retval;
805
      }
806 807

      return 1;
808
    }
809 810

    // Command to start progress for a build
811
    if (args[1] == "cmake_progress_start" && args.size() == 4) {
812 813 814
      // basically remove the directory
      std::string dirName = args[2];
      dirName += "/Progress";
815
      cmSystemTools::RemoveADirectory(dirName);
816 817

      // is the last argument a filename that exists?
818
      FILE* countFile = cmsys::SystemTools::Fopen(args[3], "r");
819
      int count;
820 821
      if (countFile) {
        if (1 != fscanf(countFile, "%i", &count)) {
822 823
          cmSystemTools::Message("Could not read from count file.");
        }
824 825
        fclose(countFile);
      } else {
826
        count = atoi(args[3].c_str());
827 828
      }
      if (count) {
829
        cmSystemTools::MakeDirectory(dirName);
830 831 832
        // write the count into the directory
        std::string fName = dirName;
        fName += "/count.txt";
833 834 835
        FILE* progFile = cmsys::SystemTools::Fopen(fName, "w");
        if (progFile) {
          fprintf(progFile, "%i\n", count);
836 837 838
          fclose(progFile);
        }
      }
839 840
      return 0;
    }
841 842

    // Command to report progress for a build
843
    if (args[1] == "cmake_progress_report" && args.size() >= 3) {
844 845 846
      // This has been superseded by cmake_echo_color --progress-*
      // options.  We leave it here to avoid errors if somehow this
      // is invoked by an existing makefile without regenerating.
847
      return 0;
848
    }
849 850 851

    // Command to create a symbolic link.  Fails on platforms not
    // supporting them.
852
    if (args[1] == "create_symlink" && args.size() == 4) {
853
      const char* destinationFileName = args[3].c_str();
854 855 856
      if ((cmSystemTools::FileExists(destinationFileName) ||
           cmSystemTools::FileIsSymlink(destinationFileName)) &&
          !cmSystemTools::RemoveFile(destinationFileName)) {
857
        std::string emsg = cmSystemTools::GetLastSystemError();
858 859 860
        std::cerr << "failed to create symbolic link '" << destinationFileName
                  << "' because existing path cannot be removed: " << emsg
                  << "\n";
861
        return 1;
862 863
      }
      if (!cmSystemTools::CreateSymlink(args[2], args[3])) {
864
        std::string emsg = cmSystemTools::GetLastSystemError();
865 866
        std::cerr << "failed to create symbolic link '" << destinationFileName
                  << "': " << emsg << "\n";
867
        return 1;
868
      }
869 870
      return 0;
    }
871 872

    // Internal CMake shared library support.
873
    if (args[1] == "cmake_symlink_library" && args.size() == 5) {
874
      return cmcmd::SymlinkLibrary(args);
875
    }
876

877
    // Internal CMake versioned executable support.
878
    if (args[1] == "cmake_symlink_executable" && args.size() == 4) {
879
      return cmcmd::SymlinkExecutable(args);
880
    }
881 882

    // Internal CMake dependency scanning support.
883
    if (args[1] == "cmake_depends" && args.size() >= 6) {
884
      const bool verbose = isCMakeVerbose();
885 886

      // Create a cmake object instance to process dependencies.
887
      cmake cm(cmake::RoleScript); // All we need is the `set` command.
888 889 890 891 892 893 894
      std::string gen;
      std::string homeDir;
      std::string startDir;
      std::string homeOutDir;
      std::string startOutDir;
      std::string depInfo;
      bool color = false;
895
      if (args.size() >= 8) {
896 897 898 899 900 901 902 903 904 905 906 907 908 909
        // Full signature:
        //
        //   -E cmake_depends <generator>
        //                    <home-src-dir> <start-src-dir>
        //                    <home-out-dir> <start-out-dir>
        //                    <dep-info> [--color=$(COLOR)]
        //
        // All paths are provided.
        gen = args[2];
        homeDir = args[3];
        startDir = args[4];
        homeOutDir = args[5];
        startOutDir = args[6];
        depInfo = args[7];
910 911
        if (args.size() >= 9 && args[8].length() >= 8 &&
            args[8].substr(0, 8) == "--color=") {
912 913 914 915
          // Enable or disable color based on the switch value.
          color = (args[8].size() == 8 ||
                   cmSystemTools::IsOn(args[8].substr(8).c_str()));
        }
916
      } else {
917 918 919 920 921 922 923 924 925 926 927 928 929 930
        // Support older signature for existing makefiles:
        //
        //   -E cmake_depends <generator>
        //                    <home-out-dir> <start-out-dir>
        //                    <dep-info>
        //
        // Just pretend the source directories are the same as the
        // binary directories so at least scanning will work.
        gen = args[2];
        homeDir = args[3];
        startDir = args[4];
        homeOutDir = args[3];
        startOutDir = args[3];
        depInfo = args[5];
931
      }
932 933 934

      // Create a local generator configured for the directory in
      // which dependencies will be scanned.
935 936 937 938
      homeDir = cmSystemTools::CollapseFullPath(homeDir);
      startDir = cmSystemTools::CollapseFullPath(startDir);
      homeOutDir = cmSystemTools::CollapseFullPath(homeOutDir);
      startOutDir = cmSystemTools::CollapseFullPath(startOutDir);
Stephen Kelly's avatar
Stephen Kelly committed
939 940
      cm.SetHomeDirectory(homeDir);
      cm.SetHomeOutputDirectory(homeOutDir);
941
      cm.GetCurrentSnapshot().SetDefaultDefinitions();
942
      if (cmGlobalGenerator* ggd = cm.CreateGlobalGenerator(gen)) {
943
        cm.SetGlobalGenerator(ggd);
944
        cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
945 946
        snapshot.GetDirectory().SetCurrentBinary(startOutDir);
        snapshot.GetDirectory().SetCurrentSource(startDir);
947 948
        cmMakefile mf(ggd, snapshot);
        std::unique_ptr<cmLocalGenerator> lgd(ggd->CreateLocalGenerator(&mf));
949 950

        // Actually scan dependencies.
951 952
        return lgd->UpdateDependencies(depInfo.c_str(), verbose, color) ? 0
                                                                        : 2;
953
      }
954 955
      return 1;
    }
956 957

    // Internal CMake link script support.
958
    if (args[1] == "cmake_link_script" && args.size() >= 3) {
959
      return cmcmd::ExecuteLinkScript(args);
960
    }
961

962 963
#ifdef CMAKE_BUILD_WITH_CMAKE
    // Internal CMake ninja dependency scanning support.
964
    if (args[1] == "cmake_ninja_depends") {
965 966
      return cmcmd_cmake_ninja_depends(args.begin() + 2, args.end());
    }
967 968

    // Internal CMake ninja dyndep support.
969
    if (args[1] == "cmake_ninja_dyndep") {
970 971
      return cmcmd_cmake_ninja_dyndep(args.begin() + 2, args.end());
    }
972 973
#endif

974
    // Internal CMake unimplemented feature notification.
975
    if (args[1] == "cmake_unimplemented_variable") {
976
      std::cerr << "Feature not implemented for this platform.";
977
      if (args.size() == 3) {
978
        std::cerr << "  Variable " << args[2] << " is not set.";
979
      }
980 981
      std::cerr << std::endl;
      return 1;
982 983 984
    }

    if (args[1] == "vs_link_exe") {
985
      return cmcmd::VisualStudioLink(args, 1);
986 987 988
    }

    if (args[1] == "vs_link_dll") {
989
      return cmcmd::VisualStudioLink(args, 2);
990
    }
991

992
    // Internal CMake color makefile support.
993
    if (args[1] == "cmake_echo_color") {
994
      return cmcmd::ExecuteEchoColor(args);
995
    }
996

997
#ifdef CMAKE_BUILD_WITH_CMAKE
998
    if ((args[1] == "cmake_autogen") && (args.size() >= 4)) {
999
      cmQtAutoGeneratorMocUic autoGen;
1000
      std::string const& infoDir = args[2];
1001
      std::string const& config = args[3];
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
      return autoGen.Run(infoDir, config) ? 0 : 1;
    }
    if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) {
      cmQtAutoGeneratorRcc autoGen;
      std::string const& infoFile = args[2];
      std::string config;
      if (args.size() > 3) {
        config = args[3];
      };
      return autoGen.Run(infoFile, config) ? 0 : 1;
1012
    }
1013 1014 1015
#endif

    // Tar files
1016
    if (args[1] == "tar" && args.size() > 3) {