joeydev/src/results.c
2023-05-23 19:22:26 -05:00

348 lines
10 KiB
C

/*
* JoeyDev
* Copyright (C) 2018-2023 Scott Duensing <scott@kangaroopunch.com>
*
* 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.
*/
#pragma clang diagnostic push
#pragma ide diagnostic ignored "cert-err34-c" // atoi warnings
#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 *lstRawDebug;
GtkWidget *lstRawRelease;
} ResultsDataT;
static ResultsDataT **_resultWindows = NULL;
EVENT void resultClicked(GtkButton *widget, gpointer userData);
static void resultLoadRaw(gboolean release, int target, int arch, ResultsDataT *self);
static void resultParseRaw(gboolean release, char *line, 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;
// ***TODO*** Highlight the button somehow so we know what we're viewing.
// Extract button info from widget name.
r = (name[0] == 'r');
t = atoi(&name[1]);
a = atoi(strstr(name, "a") + 1);
resultLoadRaw(r, t, a, userData);
}
static void resultLoadRaw(gboolean release, int target, int arch, ResultsDataT *self) {
FILE *in;
char *temp;
char *line = NULL;
size_t len = 0;
TargetT *t;
// Clear existing contents.
utilClearContainer(GTK_CONTAINER(release ? self->lstRelease : self->lstDebug));
utilClearContainer(GTK_CONTAINER(release ? self->lstRawRelease : self->lstRawDebug));
// Load raw output 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 (utilGetLine(&line, &len, in) != -1) {
if (strlen(line) > 0) line[strlen(line) - 1] = 0;
utilAddTextToListBox(GTK_LIST_BOX(release ? self->lstRawRelease : self->lstRawDebug), line, FALSE);
resultParseRaw(release, line, self);
}
}
fclose(in);
DEL(line);
}
DEL(temp);
// Display appropriate tab.
gtk_notebook_set_current_page(GTK_NOTEBOOK(self->notebookResults), release ? 1 : 0);
}
static void resultParseRaw(gboolean release, char *line, ResultsDataT *self) {
GtkWidget *messages;
GtkWidget *row;
GtkWidget *box;
GtkWidget *label;
GtkWidget *button;
char *temp;
char *text = NULL;
gboolean isWarning = FALSE;
messages = release ? self->lstRelease : self->lstDebug;
// ***TODO*** This is going to need to be a lot more complicated.
// We need the line number and filename from multiple compilers.
if (strstr(line, " warning: ") != NULL) {
text = line;
isWarning = TRUE;
}
if (strstr(line, " error: ") != NULL) {
text = line;
}
if (text) {
// New listbox row.
row = gtk_list_box_row_new();
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
gtk_widget_set_hexpand(box, TRUE);
// Icon button.
button = gtk_button_new_from_icon_name(isWarning ? "dialog-warning" : "dialog-error", GTK_ICON_SIZE_BUTTON);
temp = utilCreateString("%c", isWarning ? 'w' : 'e'); // We store data about this button in its name.
gtk_widget_set_name(button, temp);
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
DEL(temp);
// Message.
label = gtk_label_new(text);
gtk_label_set_use_markup(GTK_LABEL(label), FALSE);
gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
// Add to list.
gtk_container_add(GTK_CONTAINER(row), box);
gtk_list_box_insert(GTK_LIST_BOX(messages), row, -1);
gtk_widget_show_all(GTK_WIDGET(messages));
}
}
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));
//***TODO*** This should probably iterate over what files exist not what we expect.
// We also need to figure out why the system list is empty some times and display an error/message.
// Iterate over target data and load results.
for (i=0; i<arrlen(self->project->targets); i++) {
t = self->project->targets[i];
for (j=0; j<arrlen(t->archs); 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.0f);
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.0f);
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) {
resultLoadRaw(FALSE, firstSystem, 0, self);
resultLoadRaw(TRUE, 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",
"lstBuildOutputDebug",
"lstBuildOutputRelease",
"notebookResults",
NULL
};static
GtkWidget **widgets[] = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
char *temp;
// Is there already a results window open for this project?
for (i=0; i<arrlen(_resultWindows); i++) {
if (_resultWindows[i]->project == 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;
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->lstRawDebug;
widgets[5] = &self->lstRawRelease;
widgets[6] = &self->notebookResults;
utilGetWidgetsFromMemory("/com/kangaroopunch/joeydev/BuildResults.glade", widgetNames, widgets, self);
// Register window.
utilWindowRegister(self);
// Adjust title.
temp = utilCreateString("%s - %s", self->windowData.title, self->project->projectName);
gtk_window_set_title(GTK_WINDOW(self->windowData.window), temp);
DEL(temp);
// 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; i<arrlen(_resultWindows); i++) {
if (_resultWindows[i] == self) {
arrdel(_resultWindows, i);
break;
}
}
utilWindowUnRegister(userData);
DEL(userData);
}
#pragma clang diagnostic pop