From b3dd4d6a08ebc610f1be2d99f7f5d8b2b4e18631 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Fri, 19 May 2023 19:00:39 -0500 Subject: [PATCH] Build results starting to be displayed. --- CMakeLists.txt | 1 + include/http.h | 1 - include/messages.h | 1 + include/project.h | 29 +++- include/results.h | 33 +++++ include/utils.h | 2 + src/http.c | 18 +-- src/messages.c | 48 +++---- src/project.c | 38 +----- src/results.c | 285 +++++++++++++++++++++++++++++++++++++++ src/utils.c | 31 +++++ ui/BuildResults.glade | 187 +++++++++++++++++++++++++ ui/joeydev.gresource.xml | 1 + 13 files changed, 604 insertions(+), 71 deletions(-) create mode 100644 include/results.h create mode 100644 src/results.c create mode 100644 ui/BuildResults.glade diff --git a/CMakeLists.txt b/CMakeLists.txt index 5292331..3772fdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ set(SOURCE_FILES src/editor.c src/compiler.c src/messages.c + src/results.c ) configure_file(include/config.h.in config.h) diff --git a/include/http.h b/include/http.h index f0c6f3f..0e6187a 100644 --- a/include/http.h +++ b/include/http.h @@ -44,7 +44,6 @@ typedef struct HTTPS { curl_off_t length; curl_off_t progress; HTTPCallback callback; - GtkWidget *status; } HTTPT; diff --git a/include/messages.h b/include/messages.h index 2300c48..9ff5546 100644 --- a/include/messages.h +++ b/include/messages.h @@ -35,6 +35,7 @@ typedef enum MessageTypesE MessageTypesT; void message(MessageTypesT level, char *format, ...); +void messageShow(void); void messageShutdown(void); void messageStartup(void); diff --git a/include/project.h b/include/project.h index 6685e6a..053aec6 100644 --- a/include/project.h +++ b/include/project.h @@ -24,7 +24,34 @@ #define PROJECT_H -struct ProjectDataS; +typedef struct ArchS { + char *name; + gboolean selected; +} ArchT; + +typedef struct TargetS { + char *name; + char *longName; + ArchT **archs; +} TargetT; + +typedef struct ProjectDataS { + WindowDataT windowData; + GtkWidget *treeProject; + StringHashT **recipes; + char *projectType; + char *projectName; + char *configName; + char *buildHost; + int buildHTTPPort; + int buildSSHPort; + char *buildUser; + char *buildPassword; + TargetT **targets; + void *buildResults; // Managed by results.c + GtkWidget *tempWidget; // Used to pass data around dialogs. + int tempInteger; // Used during recipe building. +} ProjectDataT; gboolean projectAddToTree(struct ProjectDataS *self, char *filename); diff --git a/include/results.h b/include/results.h new file mode 100644 index 0000000..fe92226 --- /dev/null +++ b/include/results.h @@ -0,0 +1,33 @@ +/* + * JoeyDev + * Copyright (C) 2018-2023 Scott Duensing + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + + +#ifndef RESULTS_H +#define RESULTS_H + + +#include "project.h" + + +void winBuildResultsCreate(ProjectDataT *project); + + +#endif // RESULTS_H diff --git a/include/utils.h b/include/utils.h index 6991f8e..9d23bd4 100644 --- a/include/utils.h +++ b/include/utils.h @@ -64,6 +64,8 @@ typedef struct ArchiveS { extern char __utilFilenameBuffer[FILENAME_MAX]; +void utilAddTextToListBox(GtkListBox *list, char *text); +void utilClearContainer(GtkContainer *container); char *utilCreateString(char *format, ...); char *utilCreateStringVArgs(char *format, va_list args); ArchiveT *utilDecompress(char *archive, char *outPath, archiveCallback callback, void *userData); diff --git a/src/http.c b/src/http.c index 4012ac4..6e3ad14 100644 --- a/src/http.c +++ b/src/http.c @@ -75,7 +75,13 @@ HTTPT *httpDownload(HTTPCallback callback, char *filename, char *url) { void httpShutdown(void) { - //***TODO*** Any active sessions? + // Free any active sessions. + while (arrlen(_activeHTTPs) > 0) { + DEL(_activeHTTPs[0]->filename); + DEL(_activeHTTPs[0]); + arrdel(_activeHTTPs, 0); + } + //ARRFREE(_activeHTTPs); // This is making Memwatch mad. g_idle_remove_by_data(httpUpdate); @@ -112,13 +118,11 @@ gboolean httpUpdate(gpointer userData) { // Try to get remote file size. if (dl->length == 0) { curl_easy_getinfo(eh, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &dl->length); -#ifdef DEBUG_MODE debug("CURL Length %" CURL_FORMAT_CURL_OFF_T "\n", dl->length); -#endif } if (msg->msg == CURLMSG_DONE) { // HTTP Error? - curl_easy_getinfo (eh, CURLINFO_RESPONSE_CODE, &httpCode); + curl_easy_getinfo(eh, CURLINFO_RESPONSE_CODE, &httpCode); // Finished. Tell CURL. curl_multi_remove_handle(_curlMulti, eh); curl_easy_cleanup(eh); @@ -141,10 +145,8 @@ gboolean httpUpdate(gpointer userData) { DEL(dl->filename); DEL(dl); break; -#ifdef DEBUG_MODE } else { debug("CURL: %d\n", code); -#endif } } if (stillAlive) { @@ -157,6 +159,7 @@ gboolean httpUpdate(gpointer userData) { gboolean httpWaitForDownload(void) { + //***TODO*** This whole thing is probably a bad idea. _httpTestComplete = FALSE; // This loop seriously breaks CLion's syntax highlighting for the rest of the function. while (_httpTestComplete == FALSE) { @@ -180,9 +183,8 @@ static size_t httpWrite(char *data, size_t num, size_t length, void *userp) { // Update UI. download->progress += num * length; -#ifdef DEBUG_MODE + debug("CURL Progress %" CURL_FORMAT_CURL_OFF_T " Length %" CURL_FORMAT_CURL_OFF_T "\n", download->progress, download->length); -#endif // Did the user request to cancel the transfer? /* diff --git a/src/messages.c b/src/messages.c index c9e3c34..75b40d2 100644 --- a/src/messages.c +++ b/src/messages.c @@ -27,6 +27,8 @@ #include "utils.h" +// The message window is a singleton so everything is global. +static WindowDataT *_self = NULL; static GtkWidget *_lstMessages = NULL; static char **_pendingMessages = NULL; static pthread_mutex_t _mtxMessage; @@ -95,6 +97,14 @@ static gboolean messageScroll(gpointer userData) { } +void messageShow(void) { + // Is the window open? + if (_lstMessages) { + gtk_window_present_with_time(GTK_WINDOW(_self->window), GDK_CURRENT_TIME); + } +} + + void messageShutdown(void) { g_idle_remove_by_data(messagesUpdate); pthread_mutex_destroy(&_mtxMessage); @@ -113,7 +123,6 @@ void messageStartup(void) { static gboolean messagesUpdate(gpointer userData) { - WindowDataT *self = NULL; char *widgetNames[] = { "winMessages", "lstMessages", @@ -123,9 +132,6 @@ static gboolean messagesUpdate(gpointer userData) { NULL, &_lstMessages }; - GtkWidget *row; - GtkWidget *box; - GtkWidget *label; char *string = NULL; (void)userData; @@ -144,31 +150,20 @@ static gboolean messagesUpdate(gpointer userData) { // Do we need to open the message window? if (!_lstMessages) { // Set up instance data. We only need WindowDataT since this is a "singleton" window. - self = NEW(WindowDataT); - self->closeWindow = winMessagesClose; + _self = NEW(WindowDataT); + _self->closeWindow = winMessagesClose; - widgets[0] = &self->window; - utilGetWidgetsFromMemory("/com/kangaroopunch/joeydev/Messages.glade", widgetNames, widgets, self); + widgets[0] = &_self->window; + utilGetWidgetsFromMemory("/com/kangaroopunch/joeydev/Messages.glade", widgetNames, widgets, _self); // Register window. - utilWindowRegister(self); + utilWindowRegister(_self); // Show window. - gtk_widget_show_all(self->window); + gtk_widget_show_all(_self->window); } - // Create new row with the message string. - row = gtk_list_box_row_new(); - box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); - gtk_widget_set_hexpand(box, TRUE); - label = gtk_label_new(string); - gtk_label_set_use_markup(GTK_LABEL(label), TRUE); - - // Add new row to the message box. - gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0); - gtk_container_add(GTK_CONTAINER(row), box); - gtk_list_box_insert(GTK_LIST_BOX(_lstMessages), row, -1); - gtk_widget_show_all(_lstMessages); + utilAddTextToListBox(GTK_LIST_BOX(_lstMessages), string); utilForceUpdate(); // Scroll to show new row. @@ -182,17 +177,10 @@ static gboolean messagesUpdate(gpointer userData) { EVENT void toolMessagesClearClicked(GtkWidget *widget, gpointer userData) { - GList *children; - GList *iter; - (void)widget; (void)userData; - children = gtk_container_get_children(GTK_CONTAINER(_lstMessages)); - for (iter = children; iter != NULL; iter = g_list_next(iter)) { - gtk_widget_destroy(GTK_WIDGET(iter->data)); - } - g_list_free(children); + utilClearContainer(GTK_CONTAINER(_lstMessages)); } diff --git a/src/project.c b/src/project.c index a759f1d..f56d440 100644 --- a/src/project.c +++ b/src/project.c @@ -34,6 +34,7 @@ #include "messages.h" #include "compiler.h" #include "editor.h" +#include "results.h" #define LOCAL_BUILD_RESULTS "build.tar.bz2" @@ -62,34 +63,6 @@ enum ProjectSectionTypeE { typedef enum ProjectSectionTypeE ProjectSectionTypeT; -typedef struct ArchS { - char *name; - gboolean selected; -} ArchT; - -typedef struct TargetS { - char *name; - char *longName; - ArchT **archs; -} TargetT; - -typedef struct ProjectDataS { - WindowDataT windowData; - GtkWidget *treeProject; - StringHashT **recipes; - char *projectType; - char *projectName; - char *configName; - char *buildHost; - int buildHTTPPort; - int buildSSHPort; - char *buildUser; - char *buildPassword; - TargetT **targets; - GtkWidget *tempWidget; // Used to pass data around dialogs. - int tempInteger; // Used during recipe building. -} ProjectDataT; - typedef struct SectionDataS { char *name; char *extension; @@ -301,8 +274,8 @@ static void decompressBuild(ArchiveT *archive) { unlink(temp); DEL(temp); - //***TODO*** Process build results. message(MSG_INFO, "Processing build results"); + winBuildResultsCreate(self); } @@ -1057,6 +1030,7 @@ EVENT void menuProjectBuildCookRecipes(GtkWidget *object, gpointer userData) { // Are there recipes to cook? if (arrlen(self->recipes) > 0) { + messageShow(); self->tempInteger = -1; // Tell cookFinished() we're just starting. ctx = compilerNewContext(cookFinished, self); cookFinished(&ctx); @@ -1080,6 +1054,8 @@ EVENT void menuProjectBuildBuild(GtkWidget *object, gpointer userData) { gboolean archPrinted; FILE *out; + messageShow(); + ssh = sshConnect(self->buildHost, self->buildSSHPort, self->buildUser, self->buildPassword); if (!ssh) { @@ -1425,7 +1401,7 @@ static void sendSFTP(SFTPT *sftp) { } else { // Finished! message(MSG_INFO, "Waiting for build to complete"); - g_timeout_add_seconds(5, waitForBuild, sftp); + g_timeout_add_seconds(2, waitForBuild, sftp); } } @@ -1547,7 +1523,7 @@ void winProjectCreate(void) { "winProject", "treeProject", NULL - }; + };static GtkWidget **widgets[] = { NULL, NULL diff --git a/src/results.c b/src/results.c new file mode 100644 index 0000000..c99c21a --- /dev/null +++ b/src/results.c @@ -0,0 +1,285 @@ +/* + * JoeyDev + * Copyright (C) 2018-2023 Scott Duensing + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. +*/ + + +#include "common.h" +#include "results.h" +#include "utils.h" + + +typedef struct ResultsDataS { + WindowDataT windowData; + ProjectDataT *project; + GtkWidget *gridResults; + GtkWidget *notebookResults; + GtkWidget *lstDebug; + GtkWidget *lstRelease; + GtkWidget *lstRaw; + int lastSelected; +} ResultsDataT; + + +static ResultsDataT **_resultWindows = NULL; + + +EVENT void resultClicked(GtkButton *widget, gpointer userData); +static void resultShow(gboolean release, int target, int arch, ResultsDataT *self); +static void resultsUpdate(ResultsDataT *self); +EVENT gboolean winBuildResultsClose(GtkWidget *object, gpointer userData); +static void winBuildResultsDelete(gpointer userData); + + +EVENT void resultClicked(GtkButton *widget, gpointer userData) { + const char *name = gtk_widget_get_name(GTK_WIDGET(widget)); + int t; + int a; + gboolean r; + + // Extract button info from widget name. + r = (name[0] == 'r'); + t = atoi(&name[1]); + a = atoi(strstr(name, "a") + 1); + + resultShow(r, t, a, userData); +} + + +static void resultShow(gboolean release, int target, int arch, ResultsDataT *self) { + FILE *in; + char *temp; + char *line = NULL; + size_t len = 0; + TargetT *t; + + // Do we need to load new results? + if (target != self->lastSelected) { + + self->lastSelected = target; + + // Clear existing contents. + utilClearContainer(GTK_CONTAINER(self->lstDebug)); + utilClearContainer(GTK_CONTAINER(self->lstRelease)); + utilClearContainer(GTK_CONTAINER(self->lstRaw)); + + // Load raw results. + t = self->project->targets[target]; + temp = utilCreateString("%sresults%cbuild.%s.%s.%s", self->project->windowData.path, UTIL_PATH_CHAR, t->name, t->archs[arch]->name, release ? "release" : "debug"); + in = fopen(temp, "rt"); + if (in) { + while (!feof(in)) { + utilEnsureBufferSize((unsigned char **)&line, (int *)&len, 1024); // Not technically needed, but fixes a pointer warning from memmaker. + while (getline(&line, &len, in) != -1) { + if (strlen(line) > 0) line[strlen(line) - 1] = 0; + utilAddTextToListBox(GTK_LIST_BOX(self->lstRaw), line); + //***TODO*** Parse these for the other tabs. And, oops, we need two RAW tabs! + } + } + fclose(in); + DEL(line); + } + DEL(temp); + } + + // Display appropriate tab. + gtk_notebook_set_current_page(GTK_NOTEBOOK(self->notebookResults), release ? 1 : 0); +} + + +static void resultsUpdate(ResultsDataT *self) { + int i; + int j; + int x; + int gridLine = 0; + TargetT *t; + GtkWidget *w; + char *temp; + int firstSystem = -1; + gboolean debugFound; + gboolean releaseFound; + DIR *directory; + struct dirent *entry; + + utilClearContainer(GTK_CONTAINER(self->gridResults)); + + // Iterate over target data and load results. + for (i=0; iproject->targets); i++) { + t = self->project->targets[i]; + for (j=0; jarchs); j++) { + if (t->archs[j]->selected) { + + if (firstSystem < 0) { + firstSystem = i; + } + + // Add to grid. Name. + w = gtk_label_new(t->longName); + gtk_label_set_line_wrap(GTK_LABEL(w), FALSE); + gtk_label_set_xalign(GTK_LABEL(w), 1.0); + gtk_grid_attach(GTK_GRID(self->gridResults), w, 0, gridLine, 1, 1); + + // Add to grid. Arch. + w = gtk_label_new(t->archs[j]->name); + gtk_label_set_line_wrap(GTK_LABEL(w), FALSE); + gtk_label_set_xalign(GTK_LABEL(w), 1.0); + gtk_grid_attach(GTK_GRID(self->gridResults), w, 1, gridLine, 1, 1); + + // See if we got binaries for debug and release. + debugFound = FALSE; + releaseFound = FALSE; + + // This for loop is an ugly way to check both debug and release without making a function for the enclosed code. + for (x=0; x<2; x++) { + temp = utilCreateString("%sresults%c%s-%s%c%s", self->project->windowData.path, UTIL_PATH_CHAR, t->name, t->archs[j]->name, UTIL_PATH_CHAR, x == 0 ? "debug" : "release"); + directory = opendir(temp); + DEL(temp); + if (directory) { + // Anything in here that's not a directory is a binary. + while ((entry = readdir(directory))) { + if (entry->d_type == DT_REG) { + if (x == 0) { + debugFound = TRUE; + } else { + releaseFound = TRUE; + } + break; + } + } + closedir(directory); + } + } + + // Add debug button. + w = gtk_button_new_from_icon_name(debugFound ? "mail-mark-notjunk" : "mail-mark-junk", GTK_ICON_SIZE_BUTTON); + temp = utilCreateString("d%da%d", i, j); // We store data about this button in its name. + gtk_widget_set_name(w, temp); + DEL(temp); + gtk_grid_attach(GTK_GRID(self->gridResults), w, 2, gridLine, 1, 1); + g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(resultClicked), self); + + // Add release button. + w = gtk_button_new_from_icon_name(releaseFound ? "mail-mark-notjunk" : "mail-mark-junk", GTK_ICON_SIZE_BUTTON); + temp = utilCreateString("r%da%d", i, j); // We store data about this button in its name. + gtk_widget_set_name(w, temp); + DEL(temp); + gtk_grid_attach(GTK_GRID(self->gridResults), w, 3, gridLine, 1, 1); + g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(resultClicked), self); + + // Next result! + gridLine++; + } + } + } + + // Did we load anything? + if (gridLine > 0) { + resultShow(FALSE, firstSystem, 0, self); + } +} + + +EVENT gboolean winBuildResultsClose(GtkWidget *object, gpointer userData) { + // userData is not reliable due to util indirectly calling us. + WindowDataT *self = (WindowDataT *)utilGetWindowData(object); + + (void)userData; + + winBuildResultsDelete(self); + return FALSE; +} + + +void winBuildResultsCreate(ProjectDataT *project) { + int i; + ResultsDataT *self; + char *widgetNames[] = { + "winBuildResults", + "gridBuildResults", + "lstBuildMessagesDebug", + "lstBuildMessagesRelease", + "lstBuildMessagesRaw", + "notebookResults", + NULL + };static + GtkWidget **widgets[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; + + // Is there already a results window open for this project? + for (i=0; iproject == project) { + // Focus the existing window. + gtk_window_present_with_time(GTK_WINDOW(_resultWindows[i]->windowData.window), GDK_CURRENT_TIME); + resultsUpdate(_resultWindows[i]); + return; + } + } + + // Create new window and associate it with the project. + self = NEW(ResultsDataT); + self->windowData.closeWindow = winBuildResultsClose; + self->project = project; + self->lastSelected = -1; + project->buildResults = self; + + // Load widgets from XML. + widgets[0] = &self->windowData.window; + widgets[1] = &self->gridResults; + widgets[2] = &self->lstDebug; + widgets[3] = &self->lstRelease; + widgets[4] = &self->lstRaw; + widgets[5] = &self->notebookResults; + utilGetWidgetsFromMemory("/com/kangaroopunch/joeydev/BuildResults.glade", widgetNames, widgets, self); + + // Register window. + utilWindowRegister(self); + + // Draw contents. + resultsUpdate(self); + + // Show window. + gtk_widget_show_all(self->windowData.window); + + // Remember window. + arrput(_resultWindows, self); +} + + +static void winBuildResultsDelete(gpointer userData) { + ResultsDataT *self = (ResultsDataT *)userData; + int i; + + // Remove from open results window list. + for (i=0; idata)); + } + g_list_free(children); +} + + char *utilCreateString(char *format, ...) { va_list args; char *string; diff --git a/ui/BuildResults.glade b/ui/BuildResults.glade new file mode 100644 index 0000000..daca580 --- /dev/null +++ b/ui/BuildResults.glade @@ -0,0 +1,187 @@ + + + + + + False + 10 + 10 + 10 + 10 + Build Results + 800 + task-due + + + + True + False + 10 + + + + True + False + 10 + 10 + + + True + False + System + + + 0 + 0 + + + + + True + False + Arch + + + 1 + 0 + + + + + True + False + Debug + + + 2 + 0 + + + + + True + False + Release + + + 3 + 0 + + + + + False + True + 0 + + + + + True + True + + + True + True + in + + + True + False + + + True + False + + + + + + + + + True + False + Debug + + + False + + + + + True + True + in + + + True + False + + + True + False + + + + + + + 1 + + + + + True + False + Release + + + 1 + False + + + + + True + True + in + + + True + False + + + True + False + + + + + + + 2 + + + + + True + False + Raw + + + 2 + False + + + + + True + True + 1 + + + + + + diff --git a/ui/joeydev.gresource.xml b/ui/joeydev.gresource.xml index 46d4a74..5261821 100644 --- a/ui/joeydev.gresource.xml +++ b/ui/joeydev.gresource.xml @@ -9,6 +9,7 @@ Editor.glade Vector.glade Messages.glade + BuildResults.glade Logo.png