diff options
Diffstat (limited to 'Source/cmcmd.cxx')
-rw-r--r-- | Source/cmcmd.cxx | 1272 |
1 files changed, 789 insertions, 483 deletions
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index d5b086174..d05e3c81b 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -3,46 +3,58 @@ #include "cmcmd.h" #include "cmAlgorithms.h" +#include "cmDuration.h" #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmQtAutoGenerators.h" +#include "cmQtAutoMocUic.h" +#include "cmQtAutoRcc.h" +#include "cmRange.h" +#include "cmState.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmUtils.hxx" #include "cmVersion.h" -#include "cm_auto_ptr.hxx" #include "cmake.h" -#if defined(HAVE_SERVER_MODE) && HAVE_SERVER_MODE -#include "cmServer.h" -#include "cmServerConnection.h" +#if !defined(CMAKE_BOOTSTRAP) +# include "cmDependsFortran.h" // For -E cmake_copy_f90_mod callback. +# include "cmServer.h" +# include "cmServerConnection.h" #endif -#if defined(CMAKE_BUILD_WITH_CMAKE) -#include "cmDependsFortran.h" // For -E cmake_copy_f90_mod callback. -#endif +#if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32) +# include "cmsys/ConsoleBuf.hxx" + +# include "cmFileTime.h" -#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(_WIN32) -#include "bindexplib.h" +# include "bindexplib.h" #endif -#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(_WIN32) && !defined(__CYGWIN__) -#include "cmVisualStudioWCEPlatformParser.h" +#if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32) && !defined(__CYGWIN__) +# include "cmVisualStudioWCEPlatformParser.h" #endif -#include "cmConfigure.h" +#include <array> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <ctime> +#include <iostream> +#include <memory> +#include <sstream> +#include <utility> + +#include <cm/string_view> + #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" #include "cmsys/Process.h" #include "cmsys/Terminal.h" -#include <algorithm> -#include <iostream> -#include <sstream> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> + +class cmConnection; int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, std::vector<std::string>::const_iterator argEnd); @@ -53,7 +65,7 @@ void CMakeCommandUsage(const char* program) { std::ostringstream errorStream; -#ifdef CMAKE_BUILD_WITH_CMAKE +#ifndef CMAKE_BOOTSTRAP /* clang-format off */ errorStream << "cmake version " << cmVersion::GetCMakeVersion() << "\n"; @@ -73,7 +85,8 @@ void CMakeCommandUsage(const char* program) << " capabilities - Report capabilities built into cmake " "in JSON format\n" << " chdir dir cmd [args...] - run command in a given directory\n" - << " compare_files file1 file2 - check if file1 is same as file2\n" + << " compare_files [--ignore-eol] file1 file2\n" + << " - check if file1 is same as file2\n" << " copy <file>... destination - copy files to destination " "(either file or directory)\n" << " copy_directory <dir>... destination - copy content of <dir>... " @@ -88,18 +101,26 @@ void CMakeCommandUsage(const char* program) << " environment - display the current environment\n" << " make_directory <dir>... - create parent and <dir> directories\n" << " md5sum <file>... - create MD5 checksum of files\n" + << " 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" << " remove [-f] <file>... - remove the file(s), use -f to force " "it\n" - << " remove_directory dir - remove a directory and its contents\n" + << " remove_directory <dir>... - remove directories and their contents\n" << " rename oldname newname - rename a file or directory " "(on one volume)\n" << " server - start cmake in server mode\n" << " sleep <number>... - sleep for given number of seconds\n" << " tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n" << " - create or extract a tar or zip archive\n" - << " time command [args...] - run command and return elapsed time\n" - << " touch file - touch a file.\n" - << " touch_nocreate file - touch a file but do not create it.\n" + << " time command [args...] - run command and display elapsed time\n" + << " touch <file>... - touch a <file>.\n" + << " touch_nocreate <file>... - touch a <file> but do not create it.\n" + << " create_symlink old new - create a symbolic link new -> old\n" + << " true - do nothing with an exit code of 0\n" + << " false - do nothing with an exit code of 1\n" #if defined(_WIN32) && !defined(__CYGWIN__) << "Available on Windows only:\n" << " delete_regv key - delete registry value\n" @@ -108,14 +129,11 @@ void CMakeCommandUsage(const char* program) << " 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 ; /* clang-format on */ - cmSystemTools::Error(errorStream.str().c_str()); + cmSystemTools::Error(errorStream.str()); } static bool cmTarFilesFrom(std::string const& file, @@ -124,14 +142,14 @@ static bool cmTarFilesFrom(std::string const& file, if (cmSystemTools::FileIsDirectory(file)) { std::ostringstream e; e << "-E tar --files-from= file '" << file << "' is a directory"; - cmSystemTools::Error(e.str().c_str()); + cmSystemTools::Error(e.str()); return false; } cmsys::ifstream fin(file.c_str()); if (!fin) { std::ostringstream e; e << "-E tar --files-from= file '" << file << "' not found"; - cmSystemTools::Error(e.str().c_str()); + cmSystemTools::Error(e.str()); return false; } std::string line; @@ -145,7 +163,7 @@ static bool cmTarFilesFrom(std::string const& file, std::ostringstream e; e << "-E tar --files-from='" << file << "' file invalid line:\n" << line << "\n"; - cmSystemTools::Error(e.str().c_str()); + cmSystemTools::Error(e.str()); return false; } else { files.push_back(line); @@ -154,7 +172,303 @@ static bool cmTarFilesFrom(std::string const& file, return true; } -int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) +static int HandleIWYU(const std::string& runCmd, + const std::string& /* sourceFile */, + 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 = cmExpandedList(runCmd, true); + cmAppend(iwyu_cmd, 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; +} + +static int HandleTidy(const std::string& runCmd, const std::string& sourceFile, + 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 = cmExpandedList(runCmd, true); + tidy_cmd.push_back(sourceFile); + tidy_cmd.emplace_back("--"); + cmAppend(tidy_cmd, orig_cmd); + + // 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; +} + +static int HandleLWYU(const std::string& runCmd, + const std::string& /* sourceFile */, + 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.emplace_back("ldd"); + lwyu_cmd.emplace_back("-u"); + lwyu_cmd.emplace_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; +} + +static int HandleCppLint(const std::string& runCmd, + const std::string& sourceFile, + const std::vector<std::string>&) +{ + // Construct the cpplint command line. + std::vector<std::string> cpplint_cmd = cmExpandedList(runCmd, 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; + } + if (!stdOut.empty()) { + std::cerr << "Warning: cpplint diagnostics:\n"; + // Output the output from cpplint to stderr + std::cerr << stdOut; + } + + // always return 0 so the build can continue as cpplint returns non-zero + // for any warning + return 0; +} + +static int HandleCppCheck(const std::string& runCmd, + const std::string& sourceFile, + const std::vector<std::string>& orig_cmd) +{ + // Construct the cpplint command line. + std::vector<std::string> cppcheck_cmd = cmExpandedList(runCmd, true); + // extract all the -D, -U, and -I options from the compile line + for (auto const& opt : orig_cmd) { + 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) { + if (ret == 0) { + std::cerr << "Warning: cppcheck reported diagnostics:\n"; + } else { + std::cerr << "Error: cppcheck reported failure:\n"; + } + } + std::cerr << stdErr; + + return ret; +} + +using CoCompileHandler = int (*)(const std::string&, const std::string&, + const std::vector<std::string>&); + +struct CoCompiler +{ + const char* Option; + CoCompileHandler Handler; + bool NoOriginalCommand; +}; + +static const std::array<CoCompiler, 5> 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; +}; + +// called when args[0] == "__run_co_compile" +int cmcmd::HandleCoCompileCommands(std::vector<std::string> const& args) +{ + std::vector<CoCompileJob> jobs; + std::string sourceFile; // store --source= + std::vector<std::string> launchers; // store --launcher= + + // 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; + + std::vector<std::string> orig_cmd; + bool doing_options = true; + for (std::string const& arg : cmMakeRange(args).advance(2)) { + // 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; + for (CoCompiler const& cc : CoCompilers) { + size_t optionLen = strlen(cc.Option); + if (arg.compare(0, optionLen, cc.Option) == 0) { + optionFound = true; + CoCompileJob job; + job.Command = arg.substr(optionLen); + job.Handler = cc.Handler; + jobs.push_back(std::move(job)); + if (cc.NoOriginalCommand) { + runOriginalCmd = false; + } + } + } + if (!optionFound) { + if (cmHasLiteralPrefix(arg, "--source=")) { + sourceFile = arg.substr(9); + } else if (cmHasLiteralPrefix(arg, "--launcher=")) { + cmExpandList(arg.substr(11), launchers, true); + } else { + // if it was not a co-compiler or --source/--launcher then error + 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); + } + } + if (jobs.empty()) { + std::cerr << "__run_co_compile missing command to run. " + "Looking for one or more of the following:\n"; + for (CoCompiler const& cc : CoCompilers) { + std::cerr << cc.Option << "\n"; + } + return 1; + } + + if (runOriginalCmd && orig_cmd.empty()) { + std::cerr << "__run_co_compile missing compile command after --\n"; + return 1; + } + + 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; + } + } + + // if there is no original command to run return now + if (!runOriginalCmd) { + return 0; + } + + // Prepend launcher argument(s), if any + if (!launchers.empty()) { + orig_cmd.insert(orig_cmd.begin(), launchers.begin(), launchers.end()); + } + + // Now run the real compiler command and return its result value + int ret; + 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; +} + +int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args) { // IF YOU ADD A NEW COMMAND, DOCUMENT IT ABOVE and in cmakemain.cxx if (args.size() > 1) { @@ -163,18 +477,17 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // If multiple source files specified, // then destination must be directory if ((args.size() > 4) && - (!cmSystemTools::FileIsDirectory(args[args.size() - 1]))) { - std::cerr << "Error: Target (for copy command) \"" - << args[args.size() - 1] << "\" is not a directory.\n"; + (!cmSystemTools::FileIsDirectory(args.back()))) { + std::cerr << "Error: Target (for copy command) \"" << args.back() + << "\" is not a directory.\n"; return 1; } // If error occurs we want to continue copying next files. bool return_value = false; - 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"; + for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) { + if (!cmsys::SystemTools::CopyFileAlways(arg, args.back())) { + std::cerr << "Error copying file \"" << arg << "\" to \"" + << args.back() << "\".\n"; return_value = true; } } @@ -186,18 +499,17 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // If multiple source files specified, // then destination must be directory if ((args.size() > 4) && - (!cmSystemTools::FileIsDirectory(args[args.size() - 1]))) { + (!cmSystemTools::FileIsDirectory(args.back()))) { std::cerr << "Error: Target (for copy_if_different command) \"" - << args[args.size() - 1] << "\" is not a directory.\n"; + << args.back() << "\" is not a directory.\n"; return 1; } // If error occurs we want to continue copying next files. bool return_value = false; - 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"; + for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) { + if (!cmSystemTools::CopyFileIfDifferent(arg, args.back())) { + std::cerr << "Error copying file (if different) from \"" << arg + << "\" to \"" << args.back() << "\".\n"; return_value = true; } } @@ -208,10 +520,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) if (args[1] == "copy_directory" && args.size() > 3) { // If error occurs we want to continue copying next files. bool return_value = false; - for (std::string::size_type cc = 2; cc < args.size() - 1; cc++) { - if (!cmSystemTools::CopyADirectory(args[cc], args[args.size() - 1])) { - std::cerr << "Error copying directory from \"" << args[cc] - << "\" to \"" << args[args.size() - 1] << "\".\n"; + for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) { + if (!cmSystemTools::CopyADirectory(arg, args.back())) { + std::cerr << "Error copying directory from \"" << arg << "\" to \"" + << args.back() << "\".\n"; return_value = true; } } @@ -220,7 +532,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Rename a file or directory if (args[1] == "rename" && args.size() == 4) { - if (!cmSystemTools::RenameFile(args[2].c_str(), args[3].c_str())) { + if (!cmSystemTools::RenameFile(args[2], args[3])) { std::string e = cmSystemTools::GetLastSystemError(); std::cerr << "Error renaming from \"" << args[2] << "\" to \"" << args[3] << "\": " << e << "\n"; @@ -230,26 +542,30 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) } // Compare files - if (args[1] == "compare_files" && args.size() == 4) { - if (cmSystemTools::FilesDiffer(args[2], args[3])) { - std::cerr << "Files \"" << args[2] << "\" to \"" << args[3] - << "\" are different.\n"; + if (args[1] == "compare_files" && (args.size() == 4 || args.size() == 5)) { + bool filesDiffer; + if (args.size() == 4) { + filesDiffer = cmSystemTools::FilesDiffer(args[2], args[3]); + } else if (args[2] == "--ignore-eol") { + filesDiffer = cmsys::SystemTools::TextFilesDiffer(args[3], args[4]); + } else { + ::CMakeCommandUsage(args[0].c_str()); + return 1; + } + + if (filesDiffer) { + std::cerr << "Files \"" << args[args.size() - 2] << "\" to \"" + << args[args.size() - 1] << "\" are different.\n"; return 1; } return 0; } -#if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE) +#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP) else if (args[1] == "__create_def") { if (args.size() < 4) { - std::cerr - << "__create_def Usage: -E __create_def outfile.def objlistfile\n"; - return 1; - } - FILE* fout = cmsys::SystemTools::Fopen(args[2].c_str(), "w+"); - if (!fout) { - std::cerr << "could not open output .def file: " << args[2].c_str() - << "\n"; + std::cerr << "__create_def Usage: -E __create_def outfile.def " + "objlistfile [-nm=nm-path]\n"; return 1; } cmsys::ifstream fin(args[3].c_str(), std::ios::in | std::ios::binary); @@ -258,9 +574,41 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) << "\n"; return 1; } - std::string file; + std::vector<std::string> files; + { + std::string file; + cmFileTime outTime; + bool outValid = outTime.Load(args[2]); + while (cmSystemTools::GetLineFromStream(fin, file)) { + files.push_back(file); + if (outValid) { + cmFileTime inTime; + outValid = inTime.Load(file) && inTime.Older(outTime); + } + } + if (outValid) { + // The def file already exists and all input files are older than the + // existing def file. + return 0; + } + } + FILE* fout = cmsys::SystemTools::Fopen(args[2].c_str(), "w+"); + if (!fout) { + std::cerr << "could not open output .def file: " << args[2].c_str() + << "\n"; + return 1; + } bindexplib deffile; - while (cmSystemTools::GetLineFromStream(fin, file)) { + if (args.size() >= 5) { + auto a = args[4]; + if (cmHasLiteralPrefix(a, "--nm=")) { + deffile.SetNmPath(a.substr(5)); + std::cerr << a.substr(5) << "\n"; + } else { + std::cerr << "unknown argument: " << a << "\n"; + } + } + for (auto const& file : files) { std::string const& ext = cmSystemTools::GetFilenameLastExtension(file); if (cmSystemTools::LowerCase(ext) == ".def") { if (!deffile.AddDefinitionFile(file.c_str())) { @@ -277,181 +625,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) return 0; } #endif - // run include what you use command and then run the compile - // command. This is an internal undocumented option and should - // only be used by CMake itself when running iwyu. - if (args[1] == "__run_iwyu") { - if (args.size() < 3) { - std::cerr << "__run_iwyu Usage: -E __run_iwyu [--iwyu=/path/iwyu]" - " [--cpplint=/path/cpplint] [--tidy=/path/tidy]" - " -- compile command\n"; - return 1; - } - bool doing_options = true; - std::vector<std::string> orig_cmd; - std::string iwyu; - std::string tidy; - std::string sourceFile; - std::string lwyu; - std::string cpplint; - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - std::string const& arg = args[cc]; - if (arg == "--") { - doing_options = false; - } else if (doing_options && cmHasLiteralPrefix(arg, "--iwyu=")) { - iwyu = arg.substr(7); - } else if (doing_options && cmHasLiteralPrefix(arg, "--tidy=")) { - tidy = arg.substr(7); - } else if (doing_options && cmHasLiteralPrefix(arg, "--source=")) { - sourceFile = arg.substr(9); - } else if (doing_options && cmHasLiteralPrefix(arg, "--lwyu=")) { - lwyu = arg.substr(7); - } else if (doing_options && cmHasLiteralPrefix(arg, "--cpplint=")) { - cpplint = arg.substr(10); - } else if (doing_options) { - std::cerr << "__run_iwyu given unknown argument: " << arg << "\n"; - return 1; - } else { - orig_cmd.push_back(arg); - } - } - if (tidy.empty() && iwyu.empty() && lwyu.empty() && cpplint.empty()) { - std::cerr << "__run_iwyu missing --cpplint=, --iwyu=, --lwyu=, and/or" - " --tidy=\n"; - return 1; - } - if ((!cpplint.empty() || !tidy.empty()) && sourceFile.empty()) { - std::cerr << "__run_iwyu --cpplint= and/or __run_iwyu --tidy=" - " require --source=\n"; - return 1; - } - if (orig_cmd.empty() && lwyu.empty()) { - std::cerr << "__run_iwyu missing compile command after --\n"; - return 1; - } - - int ret = 0; - - if (!iwyu.empty()) { - // 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(iwyu, 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; - if (!cmSystemTools::RunSingleCommand(iwyu_cmd, CM_NULLPTR, &stdErr, - &ret, CM_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"; - } - } - - if (!tidy.empty()) { - // 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. - std::vector<std::string> tidy_cmd; - cmSystemTools::ExpandListArgument(tidy, 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, - CM_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; - } - } - if (!lwyu.empty()) { - // 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(lwyu); - - // 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; - if (!cmSystemTools::RunSingleCommand(lwyu_cmd, &stdOut, &stdErr, &ret, - CM_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; - } - } - - if (!cpplint.empty()) { - // Construct the cpplint command line. - std::vector<std::string> cpplint_cmd; - cmSystemTools::ExpandListArgument(cpplint, cpplint_cmd, true); - cpplint_cmd.push_back(sourceFile); - - // Run the cpplint command line. Capture its output. - std::string stdOut; - if (!cmSystemTools::RunSingleCommand(cpplint_cmd, &stdOut, &stdOut, - &ret, CM_NULLPTR, - cmSystemTools::OUTPUT_NONE)) { - std::cerr << "Error running '" << cpplint_cmd[0] << "': " << stdOut - << "\n"; - return 1; - } - - // Output the output from cpplint to stderr - std::cerr << stdOut; - - // If cpplint exited with an error do the same. - if (ret != 0) { - return ret; - } - } - - ret = 0; - // Now run the real compiler command and return its result value. - if (lwyu.empty() && - !cmSystemTools::RunSingleCommand( - orig_cmd, CM_NULLPTR, CM_NULLPTR, &ret, CM_NULLPTR, - cmSystemTools::OUTPUT_PASSTHROUGH)) { - std::cerr << "Error running '" << orig_cmd[0] << "'\n"; - return 1; - } - return ret; + if (args[1] == "__run_co_compile") { + return cmcmd::HandleCoCompileCommands(args); } // Echo string @@ -467,13 +642,13 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) } if (args[1] == "env") { - std::vector<std::string>::const_iterator ai = args.begin() + 2; - std::vector<std::string>::const_iterator ae = args.end(); + auto ai = args.cbegin() + 2; + auto ae = args.cend(); for (; ai != ae; ++ai) { std::string const& a = *ai; if (cmHasLiteralPrefix(a, "--unset=")) { // Unset environment variable. - cmSystemTools::UnPutEnv(a.c_str() + 8); + cmSystemTools::UnPutEnv(a.substr(8)); } else if (!a.empty() && a[0] == '-') { // Environment variable and command names cannot start in '-', // so this must be an unknown option. @@ -497,57 +672,66 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Execute command from remaining arguments. std::vector<std::string> cmd(ai, ae); int retval; - if (cmSystemTools::RunSingleCommand(cmd, CM_NULLPTR, CM_NULLPTR, &retval, - CM_NULLPTR, + if (cmSystemTools::RunSingleCommand(cmd, nullptr, nullptr, &retval, + nullptr, cmSystemTools::OUTPUT_PASSTHROUGH)) { return retval; } return 1; } -#if defined(CMAKE_BUILD_WITH_CMAKE) +#if !defined(CMAKE_BOOTSTRAP) if (args[1] == "environment") { - std::vector<std::string> env = cmSystemTools::GetEnvironmentVariables(); - std::vector<std::string>::iterator it; - for (it = env.begin(); it != env.end(); ++it) { - std::cout << *it << std::endl; + for (auto const& env : cmSystemTools::GetEnvironmentVariables()) { + std::cout << env << std::endl; } return 0; } #endif if (args[1] == "make_directory" && args.size() > 2) { - // If error occurs we want to continue copying next files. + // If an error occurs, we want to continue making directories. bool return_value = false; - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - if (!cmSystemTools::MakeDirectory(args[cc].c_str())) { - std::cerr << "Error creating directory \"" << args[cc] << "\".\n"; + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (!cmSystemTools::MakeDirectory(arg)) { + std::cerr << "Error creating directory \"" << arg << "\".\n"; return_value = true; } } return return_value; } - if (args[1] == "remove_directory" && args.size() == 3) { - if (cmSystemTools::FileIsDirectory(args[2]) && - !cmSystemTools::RemoveADirectory(args[2])) { - std::cerr << "Error removing directory \"" << args[2] << "\".\n"; - return 1; + if (args[1] == "remove_directory" && args.size() > 2) { + // If an error occurs, we want to continue removing directories. + bool return_value = false; + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (cmSystemTools::FileIsDirectory(arg)) { + if (cmSystemTools::FileIsSymlink(arg)) { + if (!cmSystemTools::RemoveFile(arg)) { + std::cerr << "Error removing directory symlink \"" << arg + << "\".\n"; + return_value = true; + } + } else if (!cmSystemTools::RemoveADirectory(arg)) { + std::cerr << "Error removing directory \"" << arg << "\".\n"; + return_value = true; + } + } } - return 0; + return return_value; } // Remove file if (args[1] == "remove" && args.size() > 2) { bool force = false; - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - if (args[cc] == "\\-f" || args[cc] == "-f") { + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (arg == "\\-f" || arg == "-f") { force = true; } else { // Complain if the file could not be removed, still exists, // and the -f option was not given. - if (!cmSystemTools::RemoveFile(args[cc]) && !force && - cmSystemTools::FileExists(args[cc].c_str())) { + if (!cmSystemTools::RemoveFile(arg) && !force && + cmSystemTools::FileExists(arg)) { return 1; } } @@ -557,8 +741,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Touch file if (args[1] == "touch" && args.size() > 2) { - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - if (!cmSystemTools::Touch(args[cc], true)) { + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (!cmSystemTools::Touch(arg, true)) { + std::cerr << "cmake -E touch: failed to update \""; + std::cerr << arg << "\".\n"; return 1; } } @@ -567,10 +753,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Touch file if (args[1] == "touch_nocreate" && args.size() > 2) { - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - // Complain if the file could not be removed, still exists, - // and the -f option was not given. - if (!cmSystemTools::Touch(args[cc], false)) { + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (!cmSystemTools::Touch(arg, false)) { + std::cerr << "cmake -E touch_nocreate: failed to update \""; + std::cerr << arg << "\".\n"; return 1; } } @@ -583,27 +769,23 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) std::cerr << "-E capabilities accepts no additional arguments\n"; return 1; } - cmake cm(cmake::RoleInternal); -#if defined(HAVE_SERVER_MODE) && HAVE_SERVER_MODE - std::cout << cm.ReportCapabilities(true); -#else - std::cout << cm.ReportCapabilities(false); -#endif + cmake cm(cmake::RoleInternal, cmState::Unknown); + std::cout << cm.ReportCapabilities(); return 0; } // Sleep command if (args[1] == "sleep" && args.size() > 2) { double total = 0; - for (size_t i = 2; i < args.size(); ++i) { + for (auto const& arg : cmMakeRange(args).advance(2)) { double num = 0.0; char unit; char extra; - int n = sscanf(args[i].c_str(), "%lg%c%c", &num, &unit, &extra); + int n = sscanf(arg.c_str(), "%lg%c%c", &num, &unit, &extra); if ((n == 1 || (n == 2 && unit == 's')) && num >= 0) { total += num; } else { - std::cerr << "Unknown sleep time format \"" << args[i] << "\".\n"; + std::cerr << "Unknown sleep time format \"" << arg << "\".\n"; return 1; } } @@ -617,13 +799,15 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) if (args[1] == "time" && args.size() > 2) { std::vector<std::string> command(args.begin() + 2, args.end()); - clock_t clock_start, clock_finish; - time_t time_start, time_finish; + clock_t clock_start; + clock_t clock_finish; + time_t time_start; + time_t time_finish; time(&time_start); clock_start = clock(); int ret = 0; - cmSystemTools::RunSingleCommand(command, CM_NULLPTR, CM_NULLPTR, &ret); + cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret); clock_finish = clock(); time(&time_finish); @@ -641,42 +825,45 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Command to calculate the md5sum of a file if (args[1] == "md5sum" && args.size() >= 3) { - char md5out[32]; - int retval = 0; - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - const char* filename = args[cc].c_str(); - // Cannot compute md5sum of a directory - if (cmSystemTools::FileIsDirectory(filename)) { - std::cerr << "Error: " << filename << " is a directory" << std::endl; - retval++; - } else if (!cmSystemTools::ComputeFileMD5(filename, md5out)) { - // To mimic md5sum behavior in a shell: - std::cerr << filename << ": No such file or directory" << std::endl; - retval++; - } else { - std::cout << std::string(md5out, 32) << " " << filename - << std::endl; - } - } - return retval; + 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); } // Command to change directory and run a program. if (args[1] == "chdir" && args.size() >= 4) { std::string const& directory = args[2]; - if (!cmSystemTools::FileExists(directory.c_str())) { - cmSystemTools::Error("Directory does not exist for chdir command: ", - args[2].c_str()); + if (!cmSystemTools::FileExists(directory)) { + cmSystemTools::Error("Directory does not exist for chdir command: " + + args[2]); return 1; } std::string command = cmWrap('"', cmMakeRange(args).advance(3), '"', " "); int retval = 0; - int timeout = 0; if (cmSystemTools::RunSingleCommand( - command.c_str(), CM_NULLPTR, CM_NULLPTR, &retval, - directory.c_str(), cmSystemTools::OUTPUT_PASSTHROUGH, timeout)) { + command, nullptr, nullptr, &retval, directory.c_str(), + cmSystemTools::OUTPUT_PASSTHROUGH, cmDuration::zero())) { return retval; } @@ -686,8 +873,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Command to start progress for a build if (args[1] == "cmake_progress_start" && args.size() == 4) { // basically remove the directory - std::string dirName = args[2]; - dirName += "/Progress"; + std::string dirName = cmStrCat(args[2], "/Progress"); cmSystemTools::RemoveADirectory(dirName); // is the last argument a filename that exists? @@ -702,10 +888,9 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) count = atoi(args[3].c_str()); } if (count) { - cmSystemTools::MakeDirectory(dirName.c_str()); + cmSystemTools::MakeDirectory(dirName); // write the count into the directory - std::string fName = dirName; - fName += "/count.txt"; + std::string fName = cmStrCat(dirName, "/count.txt"); FILE* progFile = cmsys::SystemTools::Fopen(fName, "w"); if (progFile) { fprintf(progFile, "%i\n", count); @@ -737,14 +922,21 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) return 1; } if (!cmSystemTools::CreateSymlink(args[2], args[3])) { - std::string emsg = cmSystemTools::GetLastSystemError(); - std::cerr << "failed to create symbolic link '" << destinationFileName - << "': " << emsg << "\n"; return 1; } return 0; } + // Command to do nothing with an exit code of 0. + if (args[1] == "true") { + return 0; + } + + // Command to do nothing with an exit code of 1. + if (args[1] == "false") { + return 1; + } + // Internal CMake shared library support. if (args[1] == "cmake_symlink_library" && args.size() == 5) { return cmcmd::SymlinkLibrary(args); @@ -760,7 +952,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) const bool verbose = isCMakeVerbose(); // Create a cmake object instance to process dependencies. - cmake cm(cmake::RoleScript); // All we need is the `set` command. + // All we need is the `set` command. + cmake cm(cmake::RoleScript, cmState::Unknown); std::string gen; std::string homeDir; std::string startDir; @@ -786,8 +979,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) if (args.size() >= 9 && args[8].length() >= 8 && args[8].substr(0, 8) == "--color=") { // Enable or disable color based on the switch value. - color = (args[8].size() == 8 || - cmSystemTools::IsOn(args[8].substr(8).c_str())); + color = (args[8].size() == 8 || cmIsOn(args[8].substr(8))); } } else { // Support older signature for existing makefiles: @@ -820,12 +1012,11 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); snapshot.GetDirectory().SetCurrentBinary(startOutDir); snapshot.GetDirectory().SetCurrentSource(startDir); - CM_AUTO_PTR<cmMakefile> mf(new cmMakefile(ggd, snapshot)); - CM_AUTO_PTR<cmLocalGenerator> lgd(ggd->CreateLocalGenerator(mf.get())); + cmMakefile mf(ggd, snapshot); + std::unique_ptr<cmLocalGenerator> lgd(ggd->CreateLocalGenerator(&mf)); // Actually scan dependencies. - return lgd->UpdateDependencies(depInfo.c_str(), verbose, color) ? 0 - : 2; + return lgd->UpdateDependencies(depInfo, verbose, color) ? 0 : 2; } return 1; } @@ -835,7 +1026,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) return cmcmd::ExecuteLinkScript(args); } -#ifdef CMAKE_BUILD_WITH_CMAKE +#ifndef CMAKE_BOOTSTRAP // Internal CMake ninja dependency scanning support. if (args[1] == "cmake_ninja_depends") { return cmcmd_cmake_ninja_depends(args.begin() + 2, args.end()); @@ -870,12 +1061,17 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) return cmcmd::ExecuteEchoColor(args); } -#ifdef CMAKE_BUILD_WITH_CMAKE - if (args[1] == "cmake_autogen" && args.size() >= 4) { - cmQtAutoGenerators autogen; - std::string const& config = args[3]; - bool autogenSuccess = autogen.Run(args[2], config); - return autogenSuccess ? 0 : 1; +#ifndef CMAKE_BOOTSTRAP + if ((args[1] == "cmake_autogen") && (args.size() >= 4)) { + cm::string_view const infoFile = args[2]; + cm::string_view const config = args[3]; + return cmQtAutoMocUic(infoFile, config) ? 0 : 1; + } + if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) { + cm::string_view const infoFile = args[2]; + cm::string_view const config = + (args.size() > 3) ? cm::string_view(args[3]) : cm::string_view(); + return cmQtAutoRcc(infoFile, config) ? 0 : 1; } #endif @@ -888,12 +1084,17 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) std::vector<std::string> files; std::string mtime; std::string format; + cmSystemTools::cmTarCompression compress = + cmSystemTools::TarCompressNone; + int nCompress = 0; bool doing_options = true; - for (std::string::size_type cc = 4; cc < args.size(); cc++) { - std::string const& arg = args[cc]; + for (auto const& arg : cmMakeRange(args).advance(4)) { if (doing_options && cmHasLiteralPrefix(arg, "--")) { if (arg == "--") { doing_options = false; + } else if (arg == "--zstd") { + compress = cmSystemTools::TarCompressZstd; + ++nCompress; } else if (cmHasLiteralPrefix(arg, "--mtime=")) { mtime = arg.substr(8); } else if (cmHasLiteralPrefix(arg, "--files-from=")) { @@ -903,42 +1104,61 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) } } else if (cmHasLiteralPrefix(arg, "--format=")) { format = arg.substr(9); - bool isKnown = - std::find(cmArrayBegin(knownFormats), cmArrayEnd(knownFormats), - format) != cmArrayEnd(knownFormats); - - if (!isKnown) { - cmSystemTools::Error("Unknown -E tar --format= argument: ", - format.c_str()); + if (!cmContains(knownFormats, format)) { + cmSystemTools::Error("Unknown -E tar --format= argument: " + + format); return 1; } } else { - cmSystemTools::Error("Unknown option to -E tar: ", arg.c_str()); + cmSystemTools::Error("Unknown option to -E tar: " + arg); return 1; } } else { files.push_back(arg); } } - cmSystemTools::cmTarCompression compress = - cmSystemTools::TarCompressNone; + cmSystemTools::cmTarAction action = cmSystemTools::TarActionNone; bool verbose = false; - int nCompress = 0; - if (flags.find_first_of('j') != std::string::npos) { - compress = cmSystemTools::TarCompressBZip2; - ++nCompress; - } - if (flags.find_first_of('J') != std::string::npos) { - compress = cmSystemTools::TarCompressXZ; - ++nCompress; - } - if (flags.find_first_of('z') != std::string::npos) { - compress = cmSystemTools::TarCompressGZip; - ++nCompress; + + for (auto flag : flags) { + switch (flag) { + case '-': + case 'f': { + // Keep for backward compatibility. Ignored + } break; + case 'j': { + compress = cmSystemTools::TarCompressBZip2; + ++nCompress; + } break; + case 'J': { + compress = cmSystemTools::TarCompressXZ; + ++nCompress; + } break; + case 'z': { + compress = cmSystemTools::TarCompressGZip; + ++nCompress; + } break; + case 'v': { + verbose = true; + } break; + case 't': { + action = cmSystemTools::TarActionList; + } break; + case 'c': { + action = cmSystemTools::TarActionCreate; + } break; + case 'x': { + action = cmSystemTools::TarActionExtract; + } break; + default: { + cmSystemTools::Message( + std::string("tar: Unknown argument: ") + flag, "Warning"); + } + } } if ((format == "7zip" || format == "zip") && nCompress > 0) { - cmSystemTools::Error("Can not use compression flags with format: ", - format.c_str()); + cmSystemTools::Error("Can not use compression flags with format: " + + format); return 1; } if (nCompress > 1) { @@ -946,24 +1166,24 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) "at most one flag of z, j, or J may be used"); return 1; } - if (flags.find_first_of('v') != std::string::npos) { - verbose = true; - } - - if (flags.find_first_of('t') != std::string::npos) { - if (!cmSystemTools::ListTar(outFile.c_str(), verbose)) { - cmSystemTools::Error("Problem listing tar: ", outFile.c_str()); + if (action == cmSystemTools::TarActionList) { + if (!cmSystemTools::ListTar(outFile, files, verbose)) { + cmSystemTools::Error("Problem listing tar: " + outFile); return 1; } - } else if (flags.find_first_of('c') != std::string::npos) { - if (!cmSystemTools::CreateTar(outFile.c_str(), files, compress, - verbose, mtime, format)) { - cmSystemTools::Error("Problem creating tar: ", outFile.c_str()); + } else if (action == cmSystemTools::TarActionCreate) { + if (files.empty()) { + cmSystemTools::Message("tar: No files or directories specified", + "Warning"); + } + if (!cmSystemTools::CreateTar(outFile, files, compress, verbose, mtime, + format)) { + cmSystemTools::Error("Problem creating tar: " + outFile); return 1; } - } else if (flags.find_first_of('x') != std::string::npos) { - if (!cmSystemTools::ExtractTar(outFile.c_str(), verbose)) { - cmSystemTools::Error("Problem extracting tar: ", outFile.c_str()); + } else if (action == cmSystemTools::TarActionExtract) { + if (!cmSystemTools::ExtractTar(outFile, files, verbose)) { + cmSystemTools::Error("Problem extracting tar: " + outFile); return 1; } #ifdef WIN32 @@ -982,6 +1202,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) cmSystemTools::Delay(delay); } #endif + } else { + cmSystemTools::Error("tar: No action specified. Please choose: 't' " + "(list), 'c' (create) or 'x' (extract)"); + return 1; } return 0; } @@ -992,17 +1216,15 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) bool isDebug = false; std::string pipe; - for (size_t i = 2; i < args.size(); ++i) { - const std::string& a = args[i]; - - if (a == "--experimental") { + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (arg == "--experimental") { supportExperimental = true; - } else if (a == "--debug") { + } else if (arg == "--debug") { pipe.clear(); isDebug = true; - } else if (a.substr(0, pipePrefix.size()) == pipePrefix) { + } else if (arg.substr(0, pipePrefix.size()) == pipePrefix) { isDebug = false; - pipe = a.substr(pipePrefix.size()); + pipe = arg.substr(pipePrefix.size()); if (pipe.empty()) { cmSystemTools::Error("No pipe given after --pipe="); return 2; @@ -1012,8 +1234,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) return 1; } } -#if defined(HAVE_SERVER_MODE) && HAVE_SERVER_MODE - cmServerConnection* conn; +#if !defined(CMAKE_BOOTSTRAP) + cmConnection* conn; if (isDebug) { conn = new cmServerStdIoConnection; } else { @@ -1024,7 +1246,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) if (server.Serve(&errorMessage)) { return 0; } - cmSystemTools::Error(errorMessage.c_str()); + cmSystemTools::Error(errorMessage); #else static_cast<void>(supportExperimental); static_cast<void>(isDebug); @@ -1033,7 +1255,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) return 1; } -#if defined(CMAKE_BUILD_WITH_CMAKE) +#if !defined(CMAKE_BOOTSTRAP) // Internal CMake Fortran module support. if (args[1] == "cmake_copy_f90_mod" && args.size() >= 4) { return cmDependsFortran::CopyModule(args) ? 0 : 1; @@ -1074,12 +1296,42 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) return 1; } -int cmcmd::SymlinkLibrary(std::vector<std::string>& args) +int cmcmd::HashSumFile(std::vector<std::string> const& args, + cmCryptoHash::Algo algo) +{ + if (args.size() < 3) { + return -1; + } + int retval = 0; + + for (auto const& filename : cmMakeRange(args).advance(2)) { + // Cannot compute sum of a directory + if (cmSystemTools::FileIsDirectory(filename)) { + std::cerr << "Error: " << filename << " is a directory" << std::endl; + retval++; + } else { + std::string value = cmSystemTools::ComputeFileHash(filename, algo); + if (value.empty()) { + // To mimic "md5sum/shasum" behavior in a shell: + std::cerr << filename << ": No such file or directory" << std::endl; + retval++; + } else { + std::cout << value << " " << filename << std::endl; + } + } + } + return retval; +} + +int cmcmd::SymlinkLibrary(std::vector<std::string> const& args) { int result = 0; - std::string const& realName = args[2]; - std::string const& soName = args[3]; - std::string const& name = args[4]; + std::string realName = args[2]; + std::string soName = args[3]; + std::string name = args[4]; + cmSystemTools::ConvertToUnixSlashes(realName); + cmSystemTools::ConvertToUnixSlashes(soName); + cmSystemTools::ConvertToUnixSlashes(name); if (soName != realName) { if (!cmcmd::SymlinkInternal(realName, soName)) { cmSystemTools::ReportLastSystemError("cmake_symlink_library"); @@ -1095,7 +1347,7 @@ int cmcmd::SymlinkLibrary(std::vector<std::string>& args) return result; } -int cmcmd::SymlinkExecutable(std::vector<std::string>& args) +int cmcmd::SymlinkExecutable(std::vector<std::string> const& args) { int result = 0; std::string const& realName = args[2]; @@ -1111,12 +1363,11 @@ int cmcmd::SymlinkExecutable(std::vector<std::string>& args) bool cmcmd::SymlinkInternal(std::string const& file, std::string const& link) { - if (cmSystemTools::FileExists(link.c_str()) || - cmSystemTools::FileIsSymlink(link)) { + if (cmSystemTools::FileExists(link) || cmSystemTools::FileIsSymlink(link)) { cmSystemTools::RemoveFile(link); } #if defined(_WIN32) && !defined(__CYGWIN__) - return cmSystemTools::CopyFileAlways(file.c_str(), link.c_str()); + return cmSystemTools::CopyFileAlways(file, link); #else std::string linktext = cmSystemTools::GetFilenameName(file); return cmSystemTools::CreateSymlink(linktext, link); @@ -1125,14 +1376,12 @@ bool cmcmd::SymlinkInternal(std::string const& file, std::string const& link) static void cmcmdProgressReport(std::string const& dir, std::string const& num) { - std::string dirName = dir; - dirName += "/Progress"; + std::string dirName = cmStrCat(dir, "/Progress"); std::string fName; FILE* progFile; // read the count - fName = dirName; - fName += "/count.txt"; + fName = cmStrCat(dirName, "/count.txt"); progFile = cmsys::SystemTools::Fopen(fName, "r"); int count = 0; if (!progFile) { @@ -1147,8 +1396,7 @@ static void cmcmdProgressReport(std::string const& dir, std::string const& num) for (const char* c = last;; ++c) { if (*c == ',' || *c == '\0') { if (c != last) { - fName = dirName; - fName += "/"; + fName = cmStrCat(dirName, '/'); fName.append(last, c - last); progFile = cmsys::SystemTools::Fopen(fName, "w"); if (progFile) { @@ -1170,75 +1418,74 @@ static void cmcmdProgressReport(std::string const& dir, std::string const& num) } } -int cmcmd::ExecuteEchoColor(std::vector<std::string>& args) +int cmcmd::ExecuteEchoColor(std::vector<std::string> const& args) { // The arguments are - // argv[0] == <cmake-executable> - // argv[1] == cmake_echo_color + // args[0] == <cmake-executable> + // args[1] == cmake_echo_color bool enabled = true; int color = cmsysTerminal_Color_Normal; bool newline = true; std::string progressDir; - for (unsigned int i = 2; i < args.size(); ++i) { - if (args[i].find("--switch=") == 0) { + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (arg.find("--switch=") == 0) { // Enable or disable color based on the switch value. - std::string value = args[i].substr(9); + std::string value = arg.substr(9); if (!value.empty()) { - enabled = cmSystemTools::IsOn(value.c_str()); + enabled = cmIsOn(value); } - } else if (cmHasLiteralPrefix(args[i], "--progress-dir=")) { - progressDir = args[i].substr(15); - } else if (cmHasLiteralPrefix(args[i], "--progress-num=")) { + } else if (cmHasLiteralPrefix(arg, "--progress-dir=")) { + progressDir = arg.substr(15); + } else if (cmHasLiteralPrefix(arg, "--progress-num=")) { if (!progressDir.empty()) { - std::string const& progressNum = args[i].substr(15); + std::string const& progressNum = arg.substr(15); cmcmdProgressReport(progressDir, progressNum); } - } else if (args[i] == "--normal") { + } else if (arg == "--normal") { color = cmsysTerminal_Color_Normal; - } else if (args[i] == "--black") { + } else if (arg == "--black") { color = cmsysTerminal_Color_ForegroundBlack; - } else if (args[i] == "--red") { + } else if (arg == "--red") { color = cmsysTerminal_Color_ForegroundRed; - } else if (args[i] == "--green") { + } else if (arg == "--green") { color = cmsysTerminal_Color_ForegroundGreen; - } else if (args[i] == "--yellow") { + } else if (arg == "--yellow") { color = cmsysTerminal_Color_ForegroundYellow; - } else if (args[i] == "--blue") { + } else if (arg == "--blue") { color = cmsysTerminal_Color_ForegroundBlue; - } else if (args[i] == "--magenta") { + } else if (arg == "--magenta") { color = cmsysTerminal_Color_ForegroundMagenta; - } else if (args[i] == "--cyan") { + } else if (arg == "--cyan") { color = cmsysTerminal_Color_ForegroundCyan; - } else if (args[i] == "--white") { + } else if (arg == "--white") { color = cmsysTerminal_Color_ForegroundWhite; - } else if (args[i] == "--bold") { + } else if (arg == "--bold") { color |= cmsysTerminal_Color_ForegroundBold; - } else if (args[i] == "--no-newline") { + } else if (arg == "--no-newline") { newline = false; - } else if (args[i] == "--newline") { + } else if (arg == "--newline") { newline = true; } else { // Color is enabled. Print with the current color. - cmSystemTools::MakefileColorEcho(color, args[i].c_str(), newline, - enabled); + cmSystemTools::MakefileColorEcho(color, arg.c_str(), newline, enabled); } } return 0; } -int cmcmd::ExecuteLinkScript(std::vector<std::string>& args) +int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args) { // The arguments are - // argv[0] == <cmake-executable> - // argv[1] == cmake_link_script - // argv[2] == <link-script-name> - // argv[3] == --verbose=? + // args[0] == <cmake-executable> + // args[1] == cmake_link_script + // args[2] == <link-script-name> + // args[3] == --verbose=? bool verbose = false; if (args.size() >= 4) { if (args[3].find("--verbose=") == 0) { - if (!cmSystemTools::IsOff(args[3].substr(10).c_str())) { + if (!cmIsOff(args[3].substr(10))) { verbose = true; } } @@ -1277,7 +1524,7 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string>& args) } // Setup this command line. - const char* cmd[2] = { command.c_str(), CM_NULLPTR }; + const char* cmd[2] = { command.c_str(), nullptr }; cmsysProcess_SetCommand(cp, cmd); // Report the command if verbose output is enabled. @@ -1287,7 +1534,7 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string>& args) // Run the command and wait for it to exit. cmsysProcess_Execute(cp); - cmsysProcess_WaitForExit(cp, CM_NULLPTR); + cmsysProcess_WaitForExit(cp, nullptr); // Report failure if any. switch (cmsysProcess_GetState(cp)) { @@ -1309,7 +1556,7 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string>& args) break; default: break; - }; + } } // Free the process instance. @@ -1321,7 +1568,7 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string>& args) int cmcmd::WindowsCEEnvironment(const char* version, const std::string& name) { -#if defined(CMAKE_BUILD_WITH_CMAKE) && defined(_WIN32) && !defined(__CYGWIN__) +#if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32) && !defined(__CYGWIN__) cmVisualStudioWCEPlatformParser parser(name.c_str()); parser.ParseVersion(version); if (parser.Found()) { @@ -1353,6 +1600,8 @@ class cmVSLink std::string ManifestFileRC; std::string ManifestFileRes; std::string TargetFile; + std::string MtPath; + std::string RcPath; public: cmVSLink(int type, bool verbose) @@ -1375,24 +1624,32 @@ private: // For visual studio 2005 and newer manifest files need to be embedded into // exe and dll's. This code does that in such a way that incremental linking // still works. -int cmcmd::VisualStudioLink(std::vector<std::string>& args, int type) +int cmcmd::VisualStudioLink(std::vector<std::string> const& args, int type) { +#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP) + // Replace streambuf so we output in the system codepage. CMake is set up + // to output in Unicode (see SetUTF8Pipes) but the Visual Studio linker + // outputs using the system codepage so we need to change behavior when + // we run the link command. + cmsys::ConsoleBuf::Manager consoleOut(std::cout); + cmsys::ConsoleBuf::Manager consoleErr(std::cerr, true); +#endif + if (args.size() < 2) { return -1; } const bool verbose = cmSystemTools::HasEnv("VERBOSE"); std::vector<std::string> expandedArgs; - for (std::vector<std::string>::iterator i = args.begin(); i != args.end(); - ++i) { + for (std::string const& i : args) { // check for nmake temporary files - if ((*i)[0] == '@' && i->find("@CMakeFiles") != 0) { - cmsys::ifstream fin(i->substr(1).c_str()); + if (i[0] == '@' && i.find("@CMakeFiles") != 0) { + cmsys::ifstream fin(i.substr(1).c_str()); std::string line; while (cmSystemTools::GetLineFromStream(fin, line)) { cmSystemTools::ParseWindowsCommandLine(line.c_str(), expandedArgs); } } else { - expandedArgs.push_back(*i); + expandedArgs.push_back(i); } } @@ -1403,8 +1660,38 @@ int cmcmd::VisualStudioLink(std::vector<std::string>& args, int type) return vsLink.Link(); } -static bool RunCommand(const char* comment, std::vector<std::string>& command, - bool verbose, int* retCodeOut = CM_NULLPTR) +enum NumberFormat +{ + FORMAT_DECIMAL, + FORMAT_HEX +}; +struct NumberFormatter +{ + NumberFormat Format; + int Value; + NumberFormatter(NumberFormat format, int value) + : Format(format) + , Value(value) + { + } +}; +std::ostream& operator<<(std::ostream& stream, + NumberFormatter const& formatter) +{ + auto const& flags = stream.flags(); + if (formatter.Format == FORMAT_DECIMAL) { + stream << std::dec << formatter.Value; + } else { + stream << "0x" << std::hex << formatter.Value; + } + stream.flags(flags); + return stream; +} + +static bool RunCommand(const char* comment, + std::vector<std::string> const& command, bool verbose, + NumberFormat exitFormat, int* retCodeOut = nullptr, + bool (*retCodeOkay)(int) = nullptr) { if (verbose) { std::cout << comment << ":\n"; @@ -1412,32 +1699,32 @@ static bool RunCommand(const char* comment, std::vector<std::string>& command, } std::string output; int retCode = 0; - // use rc command to create .res file - bool res = - cmSystemTools::RunSingleCommand(command, &output, &output, &retCode, - CM_NULLPTR, cmSystemTools::OUTPUT_NONE); - // always print the output of the command, unless - // it is the dumb rc command banner, but if the command - // returned an error code then print the output anyway as - // the banner may be mixed with some other important information. - if (output.find("Resource Compiler Version") == std::string::npos || !res || - retCode) { - std::cout << output; - } - if (!res) { - std::cout << comment << " failed to run." << std::endl; - return false; - } - // if retCodeOut is requested then always return true - // and set the retCodeOut to retCode + bool commandResult = cmSystemTools::RunSingleCommand( + command, &output, &output, &retCode, nullptr, cmSystemTools::OUTPUT_NONE); + bool const retCodeSuccess = + retCode == 0 || (retCodeOkay && retCodeOkay(retCode)); + bool const success = commandResult && retCodeSuccess; if (retCodeOut) { - *retCodeOut = retCode; - return true; + if (commandResult || !retCodeSuccess) { + *retCodeOut = retCode; + } else { + *retCodeOut = -1; + } } - if (retCode != 0) { - std::cout << comment << " failed. with " << retCode << "\n"; + if (!success) { + std::cout << comment << ": command \"" << cmJoin(command, " ") + << "\" failed (exit code " + << NumberFormatter(exitFormat, retCode) + << ") with the following output:\n" + << output; + } else { + // always print the output of the command, unless + // it is the dumb rc command banner + if (output.find("Resource Compiler Version") == std::string::npos) { + std::cout << output; + } } - return retCode == 0; + return success; } bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg, @@ -1445,7 +1732,7 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg, { // Parse our own arguments. std::string intDir; - std::vector<std::string>::const_iterator arg = argBeg; + auto arg = argBeg; while (arg != argEnd && cmHasLiteralPrefix(*arg, "-")) { if (*arg == "--") { ++arg; @@ -1458,6 +1745,12 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg, } else if (cmHasLiteralPrefix(*arg, "--intdir=")) { intDir = arg->substr(9); ++arg; + } else if (cmHasLiteralPrefix(*arg, "--rc=")) { + this->RcPath = arg->substr(5); + ++arg; + } else if (cmHasLiteralPrefix(*arg, "--mt=")) { + this->MtPath = arg->substr(5); + ++arg; } else { std::cerr << "unknown argument '" << *arg << "'\n"; return false; @@ -1509,7 +1802,7 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg, } if (this->LinkGeneratesManifest) { - this->LinkCommand.push_back("/MANIFEST"); + this->LinkCommand.emplace_back("/MANIFEST"); this->LinkCommand.push_back("/MANIFESTFILE:" + this->LinkerManifestFile); } @@ -1535,6 +1828,13 @@ int cmVSLink::Link() return LinkNonIncremental(); } +static bool mtRetIsUpdate(int mtRet) +{ + // 'mt /notify_update' returns a special value (differing between + // Windows and POSIX hosts) when it updated the manifest file. + return mtRet == 0x41020001 || mtRet == 0xbb; +} + int cmVSLink::LinkIncremental() { // This follows the steps listed here: @@ -1571,8 +1871,11 @@ int cmVSLink::LinkIncremental() if (!fout) { return -1; } - fout << this->Type << " /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ " - "24 /* RT_MANIFEST */ \"" + // Insert a pragma statement to specify utf-8 encoding. + fout << "#pragma code_page(65001)\n"; + fout << this->Type + << " /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ " + "24 /* RT_MANIFEST */ \"" << absManifestFile << "\""; } @@ -1587,10 +1890,11 @@ int cmVSLink::LinkIncremental() // Compile the resource file. std::vector<std::string> rcCommand; - rcCommand.push_back(cmSystemTools::FindProgram("rc.exe")); - rcCommand.push_back("/fo" + this->ManifestFileRes); + rcCommand.push_back(this->RcPath.empty() ? "rc" : this->RcPath); + rcCommand.emplace_back("/fo"); + rcCommand.push_back(this->ManifestFileRes); rcCommand.push_back(this->ManifestFileRC); - if (!RunCommand("RC Pass 1", rcCommand, this->Verbose)) { + if (!RunCommand("RC Pass 1", rcCommand, this->Verbose, FORMAT_DECIMAL)) { return -1; } @@ -1598,26 +1902,28 @@ int cmVSLink::LinkIncremental() this->LinkCommand.push_back(this->ManifestFileRes); // Run the link command (possibly generates intermediate manifest). - if (!RunCommand("LINK Pass 1", this->LinkCommand, this->Verbose)) { + if (!RunCommand("LINK Pass 1", this->LinkCommand, this->Verbose, + FORMAT_DECIMAL)) { return -1; } // Run the manifest tool to create the final manifest. int mtRet = this->RunMT("/out:" + this->ManifestFile, true); - // If mt returns 1090650113 (or 187 on a posix host) then it updated the - // manifest file so we need to embed it again. Otherwise we are done. - if (mtRet != 1090650113 && mtRet != 187) { + // If mt returns a special value then it updated the manifest file so + // we need to embed it again. Otherwise we are done. + if (!mtRetIsUpdate(mtRet)) { return mtRet; } // Compile the resource file again. - if (!RunCommand("RC Pass 2", rcCommand, this->Verbose)) { + if (!RunCommand("RC Pass 2", rcCommand, this->Verbose, FORMAT_DECIMAL)) { return -1; } // Link incrementally again to use the updated resource. - if (!RunCommand("FINAL LINK", this->LinkCommand, this->Verbose)) { + if (!RunCommand("FINAL LINK", this->LinkCommand, this->Verbose, + FORMAT_DECIMAL)) { return -1; } return 0; @@ -1626,7 +1932,7 @@ int cmVSLink::LinkIncremental() int cmVSLink::LinkNonIncremental() { // Run the link command (possibly generates intermediate manifest). - if (!RunCommand("LINK", this->LinkCommand, this->Verbose)) { + if (!RunCommand("LINK", this->LinkCommand, this->Verbose, FORMAT_DECIMAL)) { return -1; } @@ -1644,22 +1950,22 @@ int cmVSLink::LinkNonIncremental() int cmVSLink::RunMT(std::string const& out, bool notify) { std::vector<std::string> mtCommand; - mtCommand.push_back(cmSystemTools::FindProgram("mt.exe")); - mtCommand.push_back("/nologo"); - mtCommand.push_back("/manifest"); + mtCommand.push_back(this->MtPath.empty() ? "mt" : this->MtPath); + mtCommand.emplace_back("/nologo"); + mtCommand.emplace_back("/manifest"); if (this->LinkGeneratesManifest) { mtCommand.push_back(this->LinkerManifestFile); } - mtCommand.insert(mtCommand.end(), this->UserManifests.begin(), - this->UserManifests.end()); + cmAppend(mtCommand, this->UserManifests); mtCommand.push_back(out); if (notify) { // Add an undocumented option that enables a special return // code to notify us when the manifest is modified. - mtCommand.push_back("/notify_update"); + mtCommand.emplace_back("/notify_update"); } int mtRet = 0; - if (!RunCommand("MT", mtCommand, this->Verbose, &mtRet)) { + if (!RunCommand("MT", mtCommand, this->Verbose, FORMAT_HEX, &mtRet, + mtRetIsUpdate)) { return -1; } return mtRet; |