348 lines
10 KiB
C
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
|