summaryrefslogtreecommitdiffstats
path: root/Source/cmQtAutoGenerator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmQtAutoGenerator.cxx')
-rw-r--r--Source/cmQtAutoGenerator.cxx641
1 files changed, 641 insertions, 0 deletions
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
new file mode 100644
index 0000000..1939bd4
--- /dev/null
+++ b/Source/cmQtAutoGenerator.cxx
@@ -0,0 +1,641 @@
+/* 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();
+}