123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
- #include "cmQtAutoGen.h"
- #include "cmQtAutoGenerator.h"
- #include "cmsys/FStream.hxx"
- #include "cmAlgorithms.h"
- #include "cmGlobalGenerator.h"
- #include "cmMakefile.h"
- #include "cmStateDirectory.h"
- #include "cmStateSnapshot.h"
- #include "cmSystemTools.h"
- #include "cmake.h"
- #include <algorithm>
- // -- Class methods
- void cmQtAutoGenerator::Logger::SetVerbose(bool value)
- {
- Verbose_ = value;
- }
- void cmQtAutoGenerator::Logger::SetColorOutput(bool value)
- {
- ColorOutput_ = value;
- }
- std::string cmQtAutoGenerator::Logger::HeadLine(std::string const& title)
- {
- std::string head = title;
- head += '\n';
- head.append(head.size() - 1, '-');
- head += '\n';
- return head;
- }
- void cmQtAutoGenerator::Logger::Info(GeneratorT genType,
- std::string const& message)
- {
- std::string msg = GeneratorName(genType);
- msg += ": ";
- msg += message;
- if (msg.back() != '\n') {
- msg.push_back('\n');
- }
- {
- std::lock_guard<std::mutex> lock(Mutex_);
- cmSystemTools::Stdout(msg.c_str(), msg.size());
- }
- }
- void cmQtAutoGenerator::Logger::Warning(GeneratorT genType,
- std::string const& message)
- {
- std::string msg;
- if (message.find('\n') == std::string::npos) {
- // Single line message
- msg += GeneratorName(genType);
- msg += " warning: ";
- } else {
- // Multi line message
- msg += HeadLine(GeneratorName(genType) + " warning");
- }
- // Message
- msg += message;
- if (msg.back() != '\n') {
- msg.push_back('\n');
- }
- msg.push_back('\n');
- {
- std::lock_guard<std::mutex> lock(Mutex_);
- cmSystemTools::Stdout(msg.c_str(), msg.size());
- }
- }
- void cmQtAutoGenerator::Logger::WarningFile(GeneratorT genType,
- std::string const& filename,
- std::string const& message)
- {
- std::string msg = " ";
- msg += Quoted(filename);
- msg.push_back('\n');
- // Message
- msg += message;
- Warning(genType, msg);
- }
- void cmQtAutoGenerator::Logger::Error(GeneratorT genType,
- std::string const& message)
- {
- std::string msg;
- msg += HeadLine(GeneratorName(genType) + " error");
- // Message
- msg += message;
- if (msg.back() != '\n') {
- msg.push_back('\n');
- }
- msg.push_back('\n');
- {
- std::lock_guard<std::mutex> lock(Mutex_);
- cmSystemTools::Stderr(msg.c_str(), msg.size());
- }
- }
- void cmQtAutoGenerator::Logger::ErrorFile(GeneratorT genType,
- std::string const& filename,
- std::string const& message)
- {
- std::string emsg = " ";
- emsg += Quoted(filename);
- emsg += '\n';
- // Message
- emsg += message;
- Error(genType, emsg);
- }
- void cmQtAutoGenerator::Logger::ErrorCommand(
- GeneratorT genType, std::string const& message,
- std::vector<std::string> const& command, std::string const& output)
- {
- std::string msg;
- msg.push_back('\n');
- msg += HeadLine(GeneratorName(genType) + " subprocess error");
- msg += message;
- if (msg.back() != '\n') {
- msg.push_back('\n');
- }
- msg.push_back('\n');
- msg += HeadLine("Command");
- msg += QuotedCommand(command);
- if (msg.back() != '\n') {
- msg.push_back('\n');
- }
- msg.push_back('\n');
- msg += HeadLine("Output");
- msg += output;
- if (msg.back() != '\n') {
- msg.push_back('\n');
- }
- msg.push_back('\n');
- {
- std::lock_guard<std::mutex> lock(Mutex_);
- cmSystemTools::Stderr(msg.c_str(), msg.size());
- }
- }
- std::string cmQtAutoGenerator::FileSystem::RealPath(
- std::string const& filename)
- {
- std::lock_guard<std::mutex> lock(Mutex_);
- return cmSystemTools::GetRealPath(filename);
- }
- bool cmQtAutoGenerator::FileSystem::FileExists(std::string const& filename)
- {
- std::lock_guard<std::mutex> lock(Mutex_);
- return cmSystemTools::FileExists(filename);
- }
- bool cmQtAutoGenerator::FileSystem::FileIsOlderThan(
- std::string const& buildFile, std::string const& sourceFile,
- std::string* error)
- {
- bool res(false);
- int result = 0;
- {
- std::lock_guard<std::mutex> lock(Mutex_);
- res = cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result);
- }
- if (res) {
- res = (result < 0);
- } else {
- if (error != nullptr) {
- error->append(
- "File modification time comparison failed for the files\n ");
- error->append(Quoted(buildFile));
- error->append("\nand\n ");
- error->append(Quoted(sourceFile));
- }
- }
- return res;
- }
- bool cmQtAutoGenerator::FileSystem::FileRead(std::string& content,
- std::string const& filename,
- std::string* error)
- {
- bool success = false;
- {
- std::lock_guard<std::mutex> lock(Mutex_);
- if (cmSystemTools::FileExists(filename, true)) {
- std::size_t const length = cmSystemTools::FileLength(filename);
- cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
- if (ifs) {
- if (length > 0) {
- content.resize(length);
- ifs.read(&content.front(), content.size());
- if (ifs) {
- success = true;
- } else {
- content.clear();
- if (error != nullptr) {
- error->append("Reading from the file failed.");
- }
- }
- } else {
- // Readable but empty file
- content.clear();
- success = true;
- }
- } else if (error != nullptr) {
- error->append("Opening the file for reading failed.");
- }
- } else if (error != nullptr) {
- error->append(
- "The file does not exist, is not readable or is a directory.");
- }
- }
- return success;
- }
- bool cmQtAutoGenerator::FileSystem::FileRead(GeneratorT genType,
- std::string& content,
- std::string const& filename)
- {
- std::string error;
- if (!FileRead(content, filename, &error)) {
- Log()->ErrorFile(genType, filename, error);
- return false;
- }
- return true;
- }
- bool cmQtAutoGenerator::FileSystem::FileWrite(std::string const& filename,
- std::string const& content,
- std::string* error)
- {
- bool success = false;
- // Make sure the parent directory exists
- if (MakeParentDirectory(filename)) {
- std::lock_guard<std::mutex> lock(Mutex_);
- cmsys::ofstream outfile;
- outfile.open(filename.c_str(),
- (std::ios::out | std::ios::binary | std::ios::trunc));
- if (outfile) {
- outfile << content;
- // Check for write errors
- if (outfile.good()) {
- success = true;
- } else {
- if (error != nullptr) {
- error->assign("File writing failed");
- }
- }
- } else {
- if (error != nullptr) {
- error->assign("Opening file for writing failed");
- }
- }
- } else {
- if (error != nullptr) {
- error->assign("Could not create parent directory");
- }
- }
- return success;
- }
- bool cmQtAutoGenerator::FileSystem::FileWrite(GeneratorT genType,
- std::string const& filename,
- std::string const& content)
- {
- std::string error;
- if (!FileWrite(filename, content, &error)) {
- Log()->ErrorFile(genType, filename, error);
- return false;
- }
- return true;
- }
- bool cmQtAutoGenerator::FileSystem::FileDiffers(std::string const& filename,
- std::string const& content)
- {
- bool differs = true;
- {
- std::string oldContents;
- if (FileRead(oldContents, filename)) {
- differs = (oldContents != content);
- }
- }
- return differs;
- }
- bool cmQtAutoGenerator::FileSystem::FileRemove(std::string const& filename)
- {
- std::lock_guard<std::mutex> lock(Mutex_);
- return cmSystemTools::RemoveFile(filename);
- }
- bool cmQtAutoGenerator::FileSystem::Touch(std::string const& filename)
- {
- std::lock_guard<std::mutex> lock(Mutex_);
- return cmSystemTools::Touch(filename, false);
- }
- bool cmQtAutoGenerator::FileSystem::MakeDirectory(std::string const& dirname)
- {
- std::lock_guard<std::mutex> lock(Mutex_);
- return cmSystemTools::MakeDirectory(dirname);
- }
- bool cmQtAutoGenerator::FileSystem::MakeDirectory(GeneratorT genType,
- std::string const& dirname)
- {
- if (!MakeDirectory(dirname)) {
- Log()->ErrorFile(genType, dirname, "Could not create directory");
- return false;
- }
- return true;
- }
- bool cmQtAutoGenerator::FileSystem::MakeParentDirectory(
- std::string const& filename)
- {
- bool success = true;
- std::string const dirName = cmSystemTools::GetFilenamePath(filename);
- if (!dirName.empty()) {
- success = MakeDirectory(dirName);
- }
- return success;
- }
- bool cmQtAutoGenerator::FileSystem::MakeParentDirectory(
- GeneratorT genType, std::string const& filename)
- {
- if (!MakeParentDirectory(filename)) {
- Log()->ErrorFile(genType, filename, "Could not create parent directory");
- return false;
- }
- return true;
- }
- int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::init(uv_loop_t* uv_loop,
- ReadOnlyProcessT* process)
- {
- Process_ = process;
- Target_ = nullptr;
- return UVPipe_.init(*uv_loop, 0, this);
- }
- int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::startRead(std::string* target)
- {
- Target_ = target;
- return uv_read_start(uv_stream(), &PipeT::UVAlloc, &PipeT::UVData);
- }
- void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::reset()
- {
- Process_ = nullptr;
- Target_ = nullptr;
- UVPipe_.reset();
- Buffer_.clear();
- Buffer_.shrink_to_fit();
- }
- void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVAlloc(uv_handle_t* handle,
- size_t suggestedSize,
- uv_buf_t* buf)
- {
- auto& pipe = *reinterpret_cast<PipeT*>(handle->data);
- pipe.Buffer_.resize(suggestedSize);
- buf->base = &pipe.Buffer_.front();
- buf->len = pipe.Buffer_.size();
- }
- void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVData(uv_stream_t* stream,
- ssize_t nread,
- const uv_buf_t* buf)
- {
- auto& pipe = *reinterpret_cast<PipeT*>(stream->data);
- if (nread > 0) {
- // Append data to merged output
- if ((buf->base != nullptr) && (pipe.Target_ != nullptr)) {
- pipe.Target_->append(buf->base, nread);
- }
- } else if (nread < 0) {
- // EOF or error
- auto* proc = pipe.Process_;
- // Check it this an unusual error
- if (nread != UV_EOF) {
- if (!proc->Result()->error()) {
- proc->Result()->ErrorMessage =
- "libuv reading from pipe failed with error code ";
- proc->Result()->ErrorMessage += std::to_string(nread);
- }
- }
- // Clear libuv pipe handle and try to finish
- pipe.reset();
- proc->UVTryFinish();
- }
- }
- void cmQtAutoGenerator::ProcessResultT::reset()
- {
- ExitStatus = 0;
- TermSignal = 0;
- if (!StdOut.empty()) {
- StdOut.clear();
- StdOut.shrink_to_fit();
- }
- if (!StdErr.empty()) {
- StdErr.clear();
- StdErr.shrink_to_fit();
- }
- if (!ErrorMessage.empty()) {
- ErrorMessage.clear();
- ErrorMessage.shrink_to_fit();
- }
- }
- void cmQtAutoGenerator::ReadOnlyProcessT::setup(
- ProcessResultT* result, bool mergedOutput,
- std::vector<std::string> const& command, std::string const& workingDirectory)
- {
- Setup_.WorkingDirectory = workingDirectory;
- Setup_.Command = command;
- Setup_.Result = result;
- Setup_.MergedOutput = mergedOutput;
- }
- bool cmQtAutoGenerator::ReadOnlyProcessT::start(
- uv_loop_t* uv_loop, std::function<void()>&& finishedCallback)
- {
- if (IsStarted() || (Result() == nullptr)) {
- return false;
- }
- // Reset result before the start
- Result()->reset();
- // Fill command string pointers
- if (!Setup().Command.empty()) {
- CommandPtr_.reserve(Setup().Command.size() + 1);
- for (std::string const& arg : Setup().Command) {
- CommandPtr_.push_back(arg.c_str());
- }
- CommandPtr_.push_back(nullptr);
- } else {
- Result()->ErrorMessage = "Empty command";
- }
- if (!Result()->error()) {
- if (UVPipeOut_.init(uv_loop, this) != 0) {
- Result()->ErrorMessage = "libuv stdout pipe initialization failed";
- }
- }
- if (!Result()->error()) {
- if (UVPipeErr_.init(uv_loop, this) != 0) {
- Result()->ErrorMessage = "libuv stderr pipe initialization failed";
- }
- }
- if (!Result()->error()) {
- // -- Setup process stdio options
- // stdin
- UVOptionsStdIO_[0].flags = UV_IGNORE;
- UVOptionsStdIO_[0].data.stream = nullptr;
- // stdout
- UVOptionsStdIO_[1].flags =
- static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
- UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream();
- // stderr
- UVOptionsStdIO_[2].flags =
- static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
- UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream();
- // -- Setup process options
- std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0);
- UVOptions_.exit_cb = &ReadOnlyProcessT::UVExit;
- UVOptions_.file = CommandPtr_[0];
- UVOptions_.args = const_cast<char**>(&CommandPtr_.front());
- UVOptions_.cwd = Setup_.WorkingDirectory.c_str();
- UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE;
- UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size());
- UVOptions_.stdio = &UVOptionsStdIO_.front();
- // -- Spawn process
- if (UVProcess_.spawn(*uv_loop, UVOptions_, this) != 0) {
- Result()->ErrorMessage = "libuv process spawn failed";
- }
- }
- // -- Start reading from stdio streams
- if (!Result()->error()) {
- if (UVPipeOut_.startRead(&Result()->StdOut) != 0) {
- Result()->ErrorMessage = "libuv start reading from stdout pipe failed";
- }
- }
- if (!Result()->error()) {
- if (UVPipeErr_.startRead(Setup_.MergedOutput ? &Result()->StdOut
- : &Result()->StdErr) != 0) {
- Result()->ErrorMessage = "libuv start reading from stderr pipe failed";
- }
- }
- if (!Result()->error()) {
- IsStarted_ = true;
- FinishedCallback_ = std::move(finishedCallback);
- } else {
- // Clear libuv handles and finish
- UVProcess_.reset();
- UVPipeOut_.reset();
- UVPipeErr_.reset();
- CommandPtr_.clear();
- }
- return IsStarted();
- }
- void cmQtAutoGenerator::ReadOnlyProcessT::UVExit(uv_process_t* handle,
- int64_t exitStatus,
- int termSignal)
- {
- auto& proc = *reinterpret_cast<ReadOnlyProcessT*>(handle->data);
- if (proc.IsStarted() && !proc.IsFinished()) {
- // Set error message on demand
- proc.Result()->ExitStatus = exitStatus;
- proc.Result()->TermSignal = termSignal;
- if (!proc.Result()->error()) {
- if (termSignal != 0) {
- proc.Result()->ErrorMessage = "Process was terminated by signal ";
- proc.Result()->ErrorMessage +=
- std::to_string(proc.Result()->TermSignal);
- } else if (exitStatus != 0) {
- proc.Result()->ErrorMessage = "Process failed with return value ";
- proc.Result()->ErrorMessage +=
- std::to_string(proc.Result()->ExitStatus);
- }
- }
- // Reset process handle and try to finish
- proc.UVProcess_.reset();
- proc.UVTryFinish();
- }
- }
- void cmQtAutoGenerator::ReadOnlyProcessT::UVTryFinish()
- {
- // There still might be data in the pipes after the process has finished.
- // Therefore check if the process is finished AND all pipes are closed
- // before signaling the worker thread to continue.
- if (UVProcess_.get() == nullptr) {
- if (UVPipeOut_.uv_pipe() == nullptr) {
- if (UVPipeErr_.uv_pipe() == nullptr) {
- IsFinished_ = true;
- FinishedCallback_();
- }
- }
- }
- }
- cmQtAutoGenerator::cmQtAutoGenerator()
- : FileSys_(&Logger_)
- {
- // Initialize logger
- Logger_.SetVerbose(cmSystemTools::HasEnv("VERBOSE"));
- {
- std::string colorEnv;
- cmSystemTools::GetEnv("COLOR", colorEnv);
- if (!colorEnv.empty()) {
- Logger_.SetColorOutput(cmSystemTools::IsOn(colorEnv.c_str()));
- } else {
- Logger_.SetColorOutput(true);
- }
- }
- // Initialize libuv loop
- uv_disable_stdio_inheritance();
- #ifdef CMAKE_UV_SIGNAL_HACK
- UVHackRAII_ = cm::make_unique<cmUVSignalHackRAII>();
- #endif
- UVLoop_ = cm::make_unique<uv_loop_t>();
- uv_loop_init(UVLoop());
- }
- cmQtAutoGenerator::~cmQtAutoGenerator()
- {
- // Close libuv loop
- uv_loop_close(UVLoop());
- }
- bool cmQtAutoGenerator::Run(std::string const& infoFile,
- std::string const& config)
- {
- // Info settings
- InfoFile_ = infoFile;
- cmSystemTools::ConvertToUnixSlashes(InfoFile_);
- InfoDir_ = cmSystemTools::GetFilenamePath(infoFile);
- InfoConfig_ = config;
- bool success = false;
- {
- cmake cm(cmake::RoleScript);
- cm.SetHomeOutputDirectory(InfoDir());
- cm.SetHomeDirectory(InfoDir());
- cm.GetCurrentSnapshot().SetDefaultDefinitions();
- cmGlobalGenerator gg(&cm);
- cmStateSnapshot snapshot = cm.GetCurrentSnapshot();
- snapshot.GetDirectory().SetCurrentBinary(InfoDir());
- snapshot.GetDirectory().SetCurrentSource(InfoDir());
- auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot);
- // The OLD/WARN behavior for policy CMP0053 caused a speed regression.
- // https://gitlab.kitware.com/cmake/cmake/issues/17570
- makefile->SetPolicyVersion("3.9");
- gg.SetCurrentMakefile(makefile.get());
- success = this->Init(makefile.get());
- }
- if (success) {
- success = this->Process();
- }
- return success;
- }
- std::string cmQtAutoGenerator::SettingsFind(std::string const& content,
- const char* key)
- {
- std::string prefix(key);
- prefix += ':';
- std::string::size_type pos = content.find(prefix);
- if (pos != std::string::npos) {
- pos += prefix.size();
- if (pos < content.size()) {
- std::string::size_type posE = content.find('\n', pos);
- if ((posE != std::string::npos) && (posE != pos)) {
- return content.substr(pos, posE - pos);
- }
- }
- }
- return std::string();
- }
|