Merge topic 'ccmake_progress_bar_and_log_display'

2086da1713 ccmake: Add output and progress bar release note
66d969fcc4 ccmake: Don't overwrite the last character of the title
1d0e557aed ccmake: Display output during configure and generate
c168e789df ccmake: Use the error display for all the logs
3300070cc2 ccmake: Display an ASCII progress bar in the status bar

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !3942
This commit is contained in:
Brad King
2019-10-29 15:17:46 +00:00
committed by Kitware Robot
5 changed files with 106 additions and 52 deletions

View File

@@ -0,0 +1,6 @@
ccmake_progress_bar_and_log_display
-----------------------------------
* :manual:`ccmake(1)` now displays messages and a progress bar during
configure and generate. It will keep the output displayed if any
errors or warnings occurred.

View File

@@ -8,6 +8,7 @@
#include "cmCursesForm.h"
#include "cmCursesMainForm.h"
#include "cmCursesStandardIncludes.h"
#include "cmStringAlgorithms.h"
#include "cmVersion.h"
inline int ctrl(int z)
@@ -19,11 +20,7 @@ cmCursesLongMessageForm::cmCursesLongMessageForm(
std::vector<std::string> const& messages, const char* title)
{
// Append all messages into on big string
for (std::string const& message : messages) {
this->Messages += message;
// Add one blank line after each message
this->Messages += "\n\n";
}
this->Messages = cmJoin(messages, "\n");
this->Title = title;
this->Fields[0] = nullptr;
this->Fields[1] = nullptr;
@@ -48,7 +45,7 @@ void cmCursesLongMessageForm::UpdateStatusBar()
size = cmCursesMainForm::MAX_WIDTH - 1;
}
strncpy(bar, this->Title.c_str(), size);
for (size_t i = size - 1; i < cmCursesMainForm::MAX_WIDTH; i++) {
for (size_t i = size; i < cmCursesMainForm::MAX_WIDTH; i++) {
bar[i] = ' ';
}
int width;
@@ -139,7 +136,6 @@ void cmCursesLongMessageForm::Render(int /*left*/, int /*top*/, int /*width*/,
form_driver(this->Form, REQ_BEG_FIELD);
this->UpdateStatusBar();
this->PrintKeys();
touchwin(stdscr);
refresh();
}
@@ -153,6 +149,7 @@ void cmCursesLongMessageForm::HandleInput()
char debugMessage[128];
for (;;) {
this->PrintKeys();
int key = getch();
sprintf(debugMessage, "Message widget handling input, key: %d", key);
@@ -173,7 +170,16 @@ void cmCursesLongMessageForm::HandleInput()
}
this->UpdateStatusBar();
this->PrintKeys();
touchwin(stdscr);
wrefresh(stdscr);
}
}
void cmCursesLongMessageForm::ScrollDown()
{
if (this->Form) {
form_driver(this->Form, REQ_END_FIELD);
this->UpdateStatusBar();
touchwin(stdscr);
wrefresh(stdscr);
}

View File

@@ -25,6 +25,10 @@ public:
// Handle user input.
void HandleInput() override;
// Description:
// Scroll down to the end of the content
void ScrollDown();
// Description:
// Display form. Use a window of size width x height, starting
// at top, left.

View File

@@ -34,6 +34,7 @@ cmCursesMainForm::cmCursesMainForm(std::vector<std::string> args,
: Args(std::move(args))
, InitialWidth(initWidth)
{
this->HasNonStatusOutputs = false;
this->NumberOfPages = 0;
this->AdvancedMode = false;
this->NumberOfVisibleEntries = 0;
@@ -469,18 +470,21 @@ void cmCursesMainForm::UpdateStatusBar(const char* message)
void cmCursesMainForm::UpdateProgress(const std::string& msg, float prog)
{
char tmp[1024];
const char* cmsg = tmp;
if (prog >= 0) {
sprintf(tmp, "%s %i%%", msg.c_str(), static_cast<int>(100 * prog));
constexpr int progressBarWidth = 40;
int progressBarCompleted = static_cast<int>(progressBarWidth * prog);
int percentCompleted = static_cast<int>(100 * prog);
this->LastProgress = (percentCompleted < 100 ? " " : "");
this->LastProgress += (percentCompleted < 10 ? " " : "");
this->LastProgress += std::to_string(percentCompleted) + "% [";
this->LastProgress.append(progressBarCompleted, '#');
this->LastProgress.append(progressBarWidth - progressBarCompleted, ' ');
this->LastProgress += "] " + msg + "...";
} else {
cmsg = msg.c_str();
this->Outputs.emplace_back(msg);
}
this->UpdateStatusBar(cmsg);
this->PrintKeys(1);
curses_move(1, 1);
touchwin(stdscr);
refresh();
this->DisplayOutputs();
}
int cmCursesMainForm::Configure(int noconfigure)
@@ -489,15 +493,15 @@ int cmCursesMainForm::Configure(int noconfigure)
int yi;
getmaxyx(stdscr, yi, xi);
curses_move(1, 1);
this->UpdateStatusBar("Configuring, please wait...");
this->PrintKeys(1);
touchwin(stdscr);
refresh();
this->CMakeInstance->SetProgressCallback(
[this](const std::string& msg, float prog) {
this->UpdateProgress(msg, prog);
});
this->ResetOutputs();
if (noconfigure == 0) {
this->UpdateProgress("Configuring", 0);
this->CMakeInstance->SetProgressCallback(
[this](const std::string& msg, float prog) {
this->UpdateProgress(msg, prog);
});
}
// always save the current gui values to disk
this->FillCacheManagerFromUI();
@@ -505,9 +509,6 @@ int cmCursesMainForm::Configure(int noconfigure)
this->CMakeInstance->GetHomeOutputDirectory());
this->LoadCache(nullptr);
// Get rid of previous errors
this->Errors = std::vector<std::string>();
// run the generate process
this->OkToGenerate = true;
int retVal;
@@ -524,7 +525,7 @@ int cmCursesMainForm::Configure(int noconfigure)
keypad(stdscr, true); /* Use key symbols as KEY_DOWN */
if (retVal != 0 || !this->Errors.empty()) {
if (retVal != 0 || this->HasNonStatusOutputs) {
// see if there was an error
if (cmSystemTools::GetErrorOccuredFlag()) {
this->OkToGenerate = false;
@@ -532,15 +533,17 @@ int cmCursesMainForm::Configure(int noconfigure)
int xx;
int yy;
getmaxyx(stdscr, yy, xx);
const char* title = "Configure produced the following output";
if (cmSystemTools::GetErrorOccuredFlag()) {
title = "Configure failed with the following output";
}
cmCursesLongMessageForm* msgs =
new cmCursesLongMessageForm(this->Errors,
cmSystemTools::GetErrorOccuredFlag()
? "Errors occurred during the last pass."
: "CMake produced the following output.");
new cmCursesLongMessageForm(this->Outputs, title);
// reset error condition
cmSystemTools::ResetErrorOccuredFlag();
CurrentForm = msgs;
msgs->Render(1, 1, xx, yy);
msgs->ScrollDown();
msgs->HandleInput();
// If they typed the wrong source directory, we report
// an error and exit
@@ -563,26 +566,21 @@ int cmCursesMainForm::Generate()
int yi;
getmaxyx(stdscr, yi, xi);
curses_move(1, 1);
this->UpdateStatusBar("Generating, please wait...");
this->PrintKeys(1);
touchwin(stdscr);
refresh();
this->ResetOutputs();
this->UpdateProgress("Generating", 0);
this->CMakeInstance->SetProgressCallback(
[this](const std::string& msg, float prog) {
this->UpdateProgress(msg, prog);
});
// Get rid of previous errors
this->Errors = std::vector<std::string>();
// run the generate process
int retVal = this->CMakeInstance->Generate();
this->CMakeInstance->SetProgressCallback(nullptr);
keypad(stdscr, true); /* Use key symbols as KEY_DOWN */
if (retVal != 0 || !this->Errors.empty()) {
if (retVal != 0 || this->HasNonStatusOutputs) {
// see if there was an error
if (cmSystemTools::GetErrorOccuredFlag()) {
this->OkToGenerate = false;
@@ -592,14 +590,15 @@ int cmCursesMainForm::Generate()
int xx;
int yy;
getmaxyx(stdscr, yy, xx);
const char* title = "Messages during last pass.";
const char* title = "Generate produced the following output";
if (cmSystemTools::GetErrorOccuredFlag()) {
title = "Errors occurred during the last pass.";
title = "Generate failed with the following output";
}
cmCursesLongMessageForm* msgs =
new cmCursesLongMessageForm(this->Errors, title);
new cmCursesLongMessageForm(this->Outputs, title);
CurrentForm = msgs;
msgs->Render(1, 1, xx, yy);
msgs->ScrollDown();
msgs->HandleInput();
// If they typed the wrong source directory, we report
// an error and exit
@@ -619,7 +618,9 @@ int cmCursesMainForm::Generate()
void cmCursesMainForm::AddError(const std::string& message,
const char* /*unused*/)
{
this->Errors.emplace_back(message);
this->Outputs.emplace_back(message);
this->HasNonStatusOutputs = true;
this->DisplayOutputs();
}
void cmCursesMainForm::RemoveEntry(const char* value)
@@ -849,7 +850,7 @@ void cmCursesMainForm::HandleInput()
}
cmCursesLongMessageForm* msgs =
new cmCursesLongMessageForm(this->HelpMessage, "Help.");
new cmCursesLongMessageForm(this->HelpMessage, "Help");
CurrentForm = msgs;
msgs->Render(1, 1, x, y);
msgs->HandleInput();
@@ -861,7 +862,7 @@ void cmCursesMainForm::HandleInput()
else if (key == 'l') {
getmaxyx(stdscr, y, x);
cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(
this->Errors, "Errors occurred during the last pass.");
this->Outputs, "CMake produced the following output");
CurrentForm = msgs;
msgs->Render(1, 1, x, y);
msgs->HandleInput();
@@ -1024,6 +1025,28 @@ void cmCursesMainForm::JumpToCacheEntry(const char* astr)
}
}
void cmCursesMainForm::ResetOutputs()
{
this->LogForm.reset();
this->Outputs.clear();
this->HasNonStatusOutputs = false;
this->LastProgress.clear();
}
void cmCursesMainForm::DisplayOutputs()
{
int xi;
int yi;
getmaxyx(stdscr, yi, xi);
auto newLogForm =
new cmCursesLongMessageForm(this->Outputs, this->LastProgress.c_str());
CurrentForm = newLogForm;
this->LogForm.reset(newLogForm);
this->LogForm->Render(1, 1, xi, yi);
this->LogForm->ScrollDown();
}
const char* cmCursesMainForm::s_ConstHelpMessage =
"CMake is used to configure and generate build files for software projects. "
"The basic steps for configuring a project with ccmake are as follows:\n\n"
@@ -1080,7 +1103,7 @@ const char* cmCursesMainForm::s_ConstHelpMessage =
" c : process the configuration files with the current options\n"
" g : generate build files and exit, only available when there are no "
"new options and no errors have been detected during last configuration.\n"
" l : shows last errors\n"
" l : shows cmake output\n"
" d : delete an option\n"
" t : toggles advanced mode. In normal mode, only the most important "
"options are shown. In advanced mode, all options are shown. We recommend "

View File

@@ -16,6 +16,7 @@
#include "cmStateTypes.h"
class cmake;
class cmCursesLongMessageForm;
/** \class cmCursesMainForm
* \brief The main page of ccmake
@@ -122,10 +123,24 @@ protected:
// Jump to the cache entry whose name matches the string.
void JumpToCacheEntry(const char* str);
// Clear and reset the output log and state
void ResetOutputs();
// Display the current progress and output
void DisplayOutputs();
// Copies of cache entries stored in the user interface
std::vector<cmCursesCacheEntryComposite> Entries;
// Errors produced during last run of cmake
std::vector<std::string> Errors;
// The form used to display logs during processing
std::unique_ptr<cmCursesLongMessageForm> LogForm;
// Output produced by the last pass
std::vector<std::string> Outputs;
// Did the last pass produced outputs of interest (errors, warnings, ...)
bool HasNonStatusOutputs;
// Last progress bar
std::string LastProgress;
// Command line arguments to be passed to cmake each time
// it is run
std::vector<std::string> Args;