Major refactoring (and overcomplicating) of compiler and messages to prepare for threaded dynamic code.

This commit is contained in:
Scott Duensing 2023-05-09 20:02:31 -05:00
parent cc6eb18baf
commit 721b23d584
8 changed files with 351 additions and 177 deletions

View file

@ -24,7 +24,57 @@
#define COMPILER_H #define COMPILER_H
int compilerRunRecipe(char *recipe, char *input, char *outputPath, void *context); #include "libtcc.h"
#include "sigsegv.h"
#if HAVE_SIGSEGV_RECOVERY
#include <setjmp.h>
#include <signal.h>
#if defined _WIN32 && !defined __CYGWIN__
// Windows doesn't have sigset_t.
typedef int sigset_t;
#define sigemptyset(set)
#define sigprocmask(how,set,oldset)
#endif
#endif
enum CompilerErrorsE {
COMPILER_ERROR_NONE = 0,
COMPILER_ERROR_ALREADY_RUNNING,
COMPILER_ERROR_IN_CODE,
COMPILER_ERROR_CANNOT_RELOCATE,
COMPILER_ERROR_NO_ENTRYPOINT,
COMPILER_ERROR_SIGHANDLER_FAILED,
COMPILER_ERROR_SEGFAULT,
COMPILER_ERROR_COUNT
};
struct CompilerContextS;
typedef void (*CompilerCallback)(struct CompilerContextS **context);
typedef struct CompilerContextS {
TCCState *s;
int compilerResult;
int programResult;
CompilerCallback callback;
void *userData;
#if HAVE_SIGSEGV_RECOVERY
volatile int runPass;
jmp_buf runJump;
sigset_t runSigSet;
#endif
} CompilerContextT;
void compilerDeleteContext(CompilerContextT **context);
gboolean compilerHadError(CompilerContextT **context);
CompilerContextT *compilerNewContext(CompilerCallback callback, void *userData);
void compilerRunRecipe(CompilerContextT *context, char *recipe, char *input, char *outputPath);
#endif // COMPILER_H #endif // COMPILER_H

View file

@ -35,6 +35,8 @@ typedef enum MessageTypesE MessageTypesT;
void message(MessageTypesT level, char *format, ...); void message(MessageTypesT level, char *format, ...);
void messageShutdown(void);
void messageStartup(void);
#endif //MESSAGES_H #endif // MESSAGES_H

View file

@ -20,10 +20,10 @@
*/ */
#include <pthread.h>
#include "common.h" #include "common.h"
#include "compiler.h" #include "compiler.h"
#include "libtcc.h"
#include "sigsegv.h"
#include "project.h" #include "project.h"
#include "messages.h" #include "messages.h"
#include "utils.h" #include "utils.h"
@ -31,156 +31,214 @@
#if HAVE_SIGSEGV_RECOVERY #if HAVE_SIGSEGV_RECOVERY
#include <setjmp.h>
#include <signal.h>
#if defined _WIN32 && !defined __CYGWIN__
// Windows doesn't have sigset_t.
typedef int sigset_t;
#define sigemptyset(set)
#define sigprocmask(how,set,oldset)
#endif
volatile int runPass = 0;
jmp_buf runRecipe;
sigset_t runSigSet;
static void sigHandlerContinuation(void *arg1, void *arg2, void *arg3); static void sigHandlerContinuation(void *arg1, void *arg2, void *arg3);
int sigHandler(void *faultAddress, int serious); int sigHandler(void *faultAddress, int serious);
#endif #endif
static CompilerContextT *_currentRunningContext = NULL; // Right now we can only have one compile running at a time.
char **___recipeTargets = NULL; char **___recipeTargets = NULL;
static void compilerErrorHandler(void *opaque, const char *msg); static void compilerErrorHandler(void *opaque, const char *msg);
void recipeAddTarget(char *target);
void recipeMessage(char *format, ...); // Exposed to dynamic code. Not static.
void recipeAddTarget(char *target);
void recipeMessage(char *format, ...);
static void compilerErrorHandler(void *opaque, const char *msg) { void compilerDeleteContext(CompilerContextT **context) {
char *isWarning = strstr(msg, " warning: "); CompilerContextT *c = *context;
tcc_delete(c->s);
(void)opaque; DEL(c);
message(isWarning == NULL ? MSG_ERROR : MSG_WARN, "%s", msg);
} }
// All the paths passed in here are expected to be complete and absolute. static void compilerErrorHandler(void *opaque, const char *msg) {
int compilerRunRecipe(char *recipe, char *input, char *outputPath, void *context) { (void)opaque;
TCCState *s;
char *oldLocation;
char c;
int x;
int result = -255;
int (*entry)(char *, char *);
___recipeTargets = NULL; message(strstr(msg, " warning: ") == NULL ? MSG_ERROR : MSG_WARN, "%s", msg);
}
s = tcc_new();
if (!s) { gboolean compilerHadError(CompilerContextT **context) {
CompilerContextT *c = *context;
if (c->compilerResult != COMPILER_ERROR_NONE) {
// Handle errors not handled elsewhere.
switch (c->compilerResult) {
case COMPILER_ERROR_ALREADY_RUNNING:
message(MSG_ERROR, "Compiled code is already running");
break;
case COMPILER_ERROR_CANNOT_RELOCATE:
message(MSG_ERROR, "Unable to link compiled code");
break;
case COMPILER_ERROR_NO_ENTRYPOINT:
message(MSG_ERROR, "Compiled code is missing entrypoint");
break;
case COMPILER_ERROR_SIGHANDLER_FAILED:
message(MSG_ERROR, "Unable to set up sigsegv handler");
break;
}
return TRUE;
}
return FALSE;
}
CompilerContextT *compilerNewContext(CompilerCallback callback, void *userData) {
CompilerContextT *c;
int x;
char ch;
c = NEW(CompilerContextT);
c->compilerResult = COMPILER_ERROR_NONE;
c->programResult = 0;
c->callback = callback;
c->userData = userData;
c->s = tcc_new();
if (!c->s) {
DEL(c);
// Something bad happened. // Something bad happened.
return -1; return NULL;
} }
// __resourcePath comes in with a trailing slash. Temporarily remove it. // __resourcePath comes in with a trailing slash. Temporarily remove it.
x = (int)strlen(__resourcePath) - 1; x = (int)strlen(__resourcePath) - 1;
c = __resourcePath[x]; ch = __resourcePath[x];
__resourcePath[x] = 0; __resourcePath[x] = 0;
tcc_set_lib_path(s, __resourcePath); tcc_set_lib_path(c->s, __resourcePath);
tcc_add_sysinclude_path(s, __resourcePath); tcc_add_sysinclude_path(c->s, __resourcePath);
__resourcePath[x] = c; __resourcePath[x] = ch;
tcc_set_options(s, "-Wall -Wno-write-strings"); tcc_set_options(c->s, "-Wall -Wno-write-strings"); // Debug options not working right: -g -bt 5
tcc_set_error_func(s, stdout, compilerErrorHandler); tcc_set_error_func(c->s, NULL, compilerErrorHandler);
tcc_set_output_type(s, TCC_OUTPUT_MEMORY); tcc_set_output_type(c->s, TCC_OUTPUT_MEMORY);
if (tcc_add_file(s, recipe) < 0) { tcc_add_symbol(c->s, "fputc", fputc);
tcc_add_symbol(c->s, "qsort", qsort);
return c;
}
void compilerRunRecipe(CompilerContextT *context, char *recipe, char *input, char *outputPath) {
char *oldLocation;
int (*entry)(char *, char *);
void *self;
// All the paths passed in here are expected to be complete and absolute.
//***TODO*** Oh my god this is so bad. The next line depends on 'userData' being set to 'self' when called from project.c.
self = context->userData;
if (_currentRunningContext != NULL) {
context->compilerResult = COMPILER_ERROR_ALREADY_RUNNING;
context->callback(&context);
return;
}
___recipeTargets = NULL;
if (tcc_add_file(context->s, recipe) < 0) {
// Errors in code. // Errors in code.
tcc_delete(s); context->compilerResult = COMPILER_ERROR_IN_CODE;
return -4; context->callback(&context);
return;
} }
tcc_add_symbol(s, "fputc", fputc); tcc_add_symbol(context->s, "___recipeTargets", ___recipeTargets);
tcc_add_symbol(s, "qsort", qsort); tcc_add_symbol(context->s, "recipeAddTarget", recipeAddTarget);
tcc_add_symbol(context->s, "recipeMessage", recipeMessage);
tcc_add_symbol(context->s, "utilCreateString", utilCreateString);
tcc_add_symbol(s, "___recipeTargets", ___recipeTargets); if (tcc_relocate(context->s, TCC_RELOCATE_AUTO) < 0) {
tcc_add_symbol(s, "recipeAddTarget", recipeAddTarget);
tcc_add_symbol(s, "recipeMessage", recipeMessage);
tcc_add_symbol(s, "utilCreateString", utilCreateString);
if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0) {
// Something bad happened. // Something bad happened.
tcc_delete(s); context->compilerResult = COMPILER_ERROR_CANNOT_RELOCATE;
return -2; context->callback(&context);
return;
} }
entry = tcc_get_symbol(s, "recipe"); entry = tcc_get_symbol(context->s, "recipe");
if (!entry) { if (!entry) {
// Something bad happened. // Something bad happened.
tcc_delete(s); context->compilerResult = COMPILER_ERROR_NO_ENTRYPOINT;
return -3; context->callback(&context);
return;
} }
getcwd(__utilFilenameBuffer, FILENAME_MAX); getcwd(__utilFilenameBuffer, FILENAME_MAX);
oldLocation = strdup(__utilFilenameBuffer); oldLocation = strdup(__utilFilenameBuffer);
chdir(outputPath); chdir(outputPath);
_currentRunningContext = context;
#if HAVE_SIGSEGV_RECOVERY #if HAVE_SIGSEGV_RECOVERY
sigset_t emptySet; sigset_t emptySet;
runPass = 0; context->runPass = 0;
if (sigsegv_install_handler(&sigHandler) < 0) if (sigsegv_install_handler(&sigHandler) < 0) {
return -5; context->compilerResult = COMPILER_ERROR_SIGHANDLER_FAILED;
context->callback(&context);
return;
}
sigemptyset(&emptySet); sigemptyset(&emptySet);
sigprocmask(SIG_BLOCK, &emptySet, &runSigSet); sigprocmask(SIG_BLOCK, &emptySet, &context->runSigSet);
switch (setjmp(runRecipe)) { switch (setjmp(context->runJump)) {
case 0: case 0:
result = entry(input, outputPath); context->programResult = entry(input, outputPath);
case 1: case 1:
sigprocmask(SIG_SETMASK, &runSigSet, NULL); sigprocmask(SIG_SETMASK, &context->runSigSet, NULL);
sigsegv_install_handler(NULL); sigsegv_install_handler(NULL);
if (runPass != 0) { if (context->runPass != 0) {
message(MSG_SEVERE, "%s caused a segmentation fault!", recipe); message(MSG_SEVERE, "%s caused a segmentation fault!", recipe);
context->compilerResult = COMPILER_ERROR_SEGFAULT;
} }
break; break;
} }
context->callback(&context);
#else #else
result = entry(input, outputPath); context->programResult = entry(input, outputPath);
context->callback(&context);
#endif #endif
_currentRunningContext = NULL;
chdir(oldLocation); chdir(oldLocation);
DEL(oldLocation); DEL(oldLocation);
while (arrlen(___recipeTargets) > 0) { while (arrlen(___recipeTargets) > 0) {
if (projectAddToTree(context, ___recipeTargets[0])) { if (projectAddToTree(self, ___recipeTargets[0])) {
utilSetDirty((WindowDataT *)context, TRUE); utilSetDirty((WindowDataT *)context, TRUE);
} }
DEL(___recipeTargets[0]); DEL(___recipeTargets[0]);
arrdel(___recipeTargets, 0); arrdel(___recipeTargets, 0);
} }
ARRFREE(___recipeTargets); ARRFREE(___recipeTargets);
tcc_delete(s);
return result;
} }
#if HAVE_SIGSEGV_RECOVERY #if HAVE_SIGSEGV_RECOVERY
static void sigHandlerContinuation(void *arg1, void *arg2, void *arg3) { static void sigHandlerContinuation(void *arg1, void *arg2, void *arg3) {
(void)arg1; CompilerContextT *c = (CompilerContextT *)arg1;
(void)arg2; (void)arg2;
(void)arg3; (void)arg3;
longjmp(runRecipe, runPass);
longjmp(c->runJump, c->runPass);
} }
@ -188,11 +246,12 @@ int sigHandler(void *faultAddress, int serious) {
(void)faultAddress; (void)faultAddress;
(void)serious; (void)serious;
runPass++; _currentRunningContext->runPass++;
return sigsegv_leave_handler(sigHandlerContinuation, NULL, NULL, NULL); return sigsegv_leave_handler(sigHandlerContinuation, _currentRunningContext, NULL, NULL);
} }
#endif #endif
void recipeAddTarget(char *target) { void recipeAddTarget(char *target) {
arrput(___recipeTargets, strdup(target)); arrput(___recipeTargets, strdup(target));
} }

View file

@ -28,6 +28,7 @@
#include "ssh.h" #include "ssh.h"
#include "utils.h" #include "utils.h"
#include "cwalk.h" #include "cwalk.h"
#include "messages.h"
char *__resourcePath = NULL; char *__resourcePath = NULL;
@ -47,6 +48,7 @@ int main(int argc, char **argv) {
gtk_init(&argc, &argv); gtk_init(&argc, &argv);
utilStartup(); utilStartup();
messageStartup();
// Make sure we have a config & resources folder. // Make sure we have a config & resources folder.
__resourcePath = utilCreateString("%s%cjoeydev%cresources%c", g_get_user_config_dir(), UTIL_PATH_CHAR, UTIL_PATH_CHAR, UTIL_PATH_CHAR); __resourcePath = utilCreateString("%s%cjoeydev%cresources%c", g_get_user_config_dir(), UTIL_PATH_CHAR, UTIL_PATH_CHAR, UTIL_PATH_CHAR);
@ -79,6 +81,7 @@ int main(int argc, char **argv) {
scintilla_release_resources(); scintilla_release_resources();
sshShutdown(); sshShutdown();
httpShutdown(); httpShutdown();
messageShutdown();
utilShutdown(); utilShutdown();
DEL(__resourcePath); DEL(__resourcePath);

View file

@ -20,21 +20,74 @@
*/ */
#include <pthread.h>
#include "common.h" #include "common.h"
#include "messages.h" #include "messages.h"
#include "utils.h" #include "utils.h"
static GtkWidget *_lstMessages = NULL; static GtkWidget *_lstMessages = NULL;
static int _autoScroll = 0; static char **_pendingMessages = NULL;
static pthread_mutex_t _mtxMessage;
static gboolean messagesScroll(gpointer userData); static gboolean messagesUpdate(gpointer userData);
EVENT gboolean winMessagesClose(GtkWidget *object, gpointer userData); EVENT gboolean winMessagesClose(GtkWidget *object, gpointer userData);
static void winMessagesDelete(gpointer userData); static void winMessagesDelete(gpointer userData);
void message(MessageTypesT level, char *format, ...) { void message(MessageTypesT level, char *format, ...) {
va_list args;
char *string;
char *msg;
char *tok;
char *labels[MSG_COUNT] = {
"<span foreground=\"gray\"> Info:</span>",
"<span foreground=\"yellow\">Warning:</span>",
"<span foreground=\"red\"> Error:</span>",
"<span foreground=\"red\"><b> Severe:</b></span>"
};
// Construct message.
va_start(args, format);
msg = utilCreateStringVArgs(format, args);
va_end(args);
// Break multiline messages down into individual lines.
tok = strtok(msg, "\n");
while (tok != NULL) {
//***TODO*** Filter out things that could be mistaken as markup tags.
string = utilCreateString("<tt>%s %s</tt>", labels[level], tok);
// Save for UI thread. message() is not always called from the UI thread!
pthread_mutex_lock(&_mtxMessage);
arrput(_pendingMessages, string); // Do not free string.
pthread_mutex_unlock(&_mtxMessage);
tok = strtok(NULL, "\n");
}
}
void messageShutdown(void) {
g_idle_remove_by_data(messagesUpdate);
pthread_mutex_destroy(&_mtxMessage);
}
void messageStartup(void) {
if (pthread_mutex_init(&_mtxMessage, NULL) != 0) {
debug("Message mutex init failed!\n");
exit(1);
}
// Set up updates on the UI thread.
g_idle_add(messagesUpdate, messagesUpdate);
}
static gboolean messagesUpdate(gpointer userData) {
WindowDataT *self = NULL; WindowDataT *self = NULL;
char *widgetNames[] = { char *widgetNames[] = {
"winMessages", "winMessages",
@ -48,83 +101,59 @@ void message(MessageTypesT level, char *format, ...) {
GtkWidget *row; GtkWidget *row;
GtkWidget *box; GtkWidget *box;
GtkWidget *label; GtkWidget *label;
va_list args; GtkAdjustment *adjustment;
char *string; char *string = NULL;
char *msg;
char *tok;
char *labels[MSG_COUNT] = {
"<span foreground=\"gray\"> Info:</span>",
"<span foreground=\"yellow\">Warning:</span>",
"<span foreground=\"red\"> Error:</span>",
"<span foreground=\"red\"><b> Severe:</b></span>"
};
// Do we need to open this window? (void)userData;
if (!_lstMessages) {
// Set up instance data. We only need WindowDataT since this is a "singleton" window.
self = NEW(WindowDataT);
self->closeWindow = winMessagesClose;
widgets[0] = &self->window; // Lock and access the pending message list.
utilGetWidgetsFromMemory("/com/kangaroopunch/joeydev/Messages.glade", widgetNames, widgets, self); pthread_mutex_lock(&_mtxMessage);
if (arrlen(_pendingMessages) > 0) {
// Register window. string = _pendingMessages[0]; // We just need the pointer.
utilWindowRegister(self); arrdel(_pendingMessages, 0);
// Show window.
gtk_widget_show_all(self->window);
// Set up automatic scrolling.
g_idle_add(messagesScroll, messagesScroll);
} }
pthread_mutex_unlock(&_mtxMessage);
// Display message. // Are there pending messages?
va_start(args, format); if (string != NULL) {
msg = utilCreateStringVArgs(format, args);
va_end(args);
// Break multiline messages down into individual lines. // Do we need to open the message window?
tok = strtok(msg, "\n"); if (!_lstMessages) {
while (tok != NULL) { // Set up instance data. We only need WindowDataT since this is a "singleton" window.
//***TODO*** Filter out things that could be mistaken as markup tags. self = NEW(WindowDataT);
string = utilCreateString("<tt>%s %s</tt>", labels[level], tok); self->closeWindow = winMessagesClose;
widgets[0] = &self->window;
utilGetWidgetsFromMemory("/com/kangaroopunch/joeydev/Messages.glade", widgetNames, widgets, self);
// Register window.
utilWindowRegister(self);
// Show window.
gtk_widget_show_all(self->window);
}
// Create new row with the message string.
row = gtk_list_box_row_new(); row = gtk_list_box_row_new();
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
gtk_widget_set_hexpand(box, TRUE); gtk_widget_set_hexpand(box, TRUE);
label = gtk_label_new(string); label = gtk_label_new(string);
gtk_label_set_use_markup(GTK_LABEL(label), TRUE); gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
DEL(string); // Add new row to the message box.
gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(row), box); gtk_container_add(GTK_CONTAINER(row), box);
gtk_list_box_insert(GTK_LIST_BOX(_lstMessages), row, -1); gtk_list_box_insert(GTK_LIST_BOX(_lstMessages), row, -1);
// Force paint. // Scroll to show new row.
gtk_widget_show_all(row); //***TODO*** This doesn't always scroll to the very end.
adjustment = gtk_list_box_get_adjustment(GTK_LIST_BOX(_lstMessages));
gtk_adjustment_set_value(adjustment, gtk_adjustment_get_upper(adjustment));
gtk_widget_show_all(_lstMessages);
utilForceUpdate(); utilForceUpdate();
_autoScroll = 15; // Try several times to show the new row.
tok = strtok(NULL, "\n"); DEL(string); // Finally free the string allocated in message().
} }
}
static gboolean messagesScroll(gpointer userData) {
GtkAdjustment *adjustment;
(void)userData;
if (_autoScroll <= 0) return G_SOURCE_CONTINUE;
// Scroll to show new line.
adjustment = gtk_list_box_get_adjustment(GTK_LIST_BOX(_lstMessages));
gtk_adjustment_set_value(adjustment, gtk_adjustment_get_upper(adjustment));
gtk_widget_show_all(_lstMessages);
utilForceUpdate();
_autoScroll--;
return G_SOURCE_CONTINUE; return G_SOURCE_CONTINUE;
} }
@ -142,7 +171,13 @@ EVENT gboolean winMessagesClose(GtkWidget *object, gpointer userData) {
static void winMessagesDelete(gpointer userData) { static void winMessagesDelete(gpointer userData) {
g_idle_remove_by_data(messagesScroll); // Delete any pending messages.
while (arrlen(_pendingMessages) > 0) {
DEL(_pendingMessages[0]);
arrdel(_pendingMessages, 0);
}
ARRFREE(_pendingMessages);
utilWindowUnRegister(userData); utilWindowUnRegister(userData);
_lstMessages = NULL; _lstMessages = NULL;
DEL(userData); DEL(userData);

View file

@ -84,7 +84,8 @@ typedef struct ProjectDataS {
char *buildUser; char *buildUser;
char *buildPassword; char *buildPassword;
TargetT **targets; TargetT **targets;
GtkWidget *tempWidget; // Used to pass data around dialogs. GtkWidget *tempWidget; // Used to pass data around dialogs.
int tempInteger; // Used during recipe building.
} ProjectDataT; } ProjectDataT;
typedef struct SectionDataS { typedef struct SectionDataS {
@ -107,7 +108,6 @@ static SectionDataT _sectionData[] = {
{ NULL, NULL } { NULL, NULL }
}; };
static ProjectDataT *_cookingProjectData = NULL;
static char **_sendList = NULL; static char **_sendList = NULL;
@ -120,6 +120,7 @@ EVENT void btnEditRawClicked(GtkButton *widget, gpointer userData);
EVENT void btnEditRecipeClicked(GtkButton *widget, gpointer userData); EVENT void btnEditRecipeClicked(GtkButton *widget, gpointer userData);
EVENT void btnNewRecipeClicked(GtkButton *widget, gpointer userData); EVENT void btnNewRecipeClicked(GtkButton *widget, gpointer userData);
static void clearRecipeData(ProjectDataT *self); static void clearRecipeData(ProjectDataT *self);
static void cookFinished(CompilerContextT **context);
static void decompressBuild(ArchiveT *archive); static void decompressBuild(ArchiveT *archive);
static void dialogCookOptions(char *filename, ProjectDataT *self); static void dialogCookOptions(char *filename, ProjectDataT *self);
static int findRecipeData(ProjectDataT *self, char *key); static int findRecipeData(ProjectDataT *self, char *key);
@ -244,6 +245,50 @@ static void clearRecipeData(ProjectDataT *self) {
} }
static void cookFinished(CompilerContextT **context) {
CompilerContextT *ctx = *context;
ProjectDataT *self = (ProjectDataT *)ctx->userData;
char *raw;
char *recipe;
// Was there a compiler error?
if (compilerHadError(context)) {
compilerDeleteContext(context);
return;
}
// If this is not first call, process context.
if (self->tempInteger != -1) {
if (ctx->programResult != 0) {
//***TODO*** Not all negative returns are severe.
message(ctx->programResult > 0 ? MSG_ERROR : MSG_SEVERE, "Recipe %s returned %d", self->recipes[self->tempInteger]->value, ctx->programResult);
}
}
compilerDeleteContext(context);
// Is there something to cook?
self->tempInteger++;
if (self->tempInteger < arrlen(self->recipes)) {
// Build path names.
cwk_path_change_basename(self->windowData.filename, self->recipes[self->tempInteger]->key, __utilFilenameBuffer, sizeof(__utilFilenameBuffer));
raw = strdup(__utilFilenameBuffer);
cwk_path_change_basename(self->windowData.filename, self->recipes[self->tempInteger]->value, __utilFilenameBuffer, sizeof(__utilFilenameBuffer));
recipe = strdup(__utilFilenameBuffer);
// Run it!
message(MSG_INFO, "Cooking %s", self->recipes[self->tempInteger]->key);
ctx = compilerNewContext(cookFinished, self);
compilerRunRecipe(ctx, recipe, raw, self->windowData.path);
DEL(recipe);
DEL(raw);
} else {
// Finished with recipe list.
message(MSG_INFO, "Finished Cooking");
}
}
static void decompressBuild(ArchiveT *archive) { static void decompressBuild(ArchiveT *archive) {
ProjectDataT *self = (ProjectDataT *)archive->userData; ProjectDataT *self = (ProjectDataT *)archive->userData;
char *temp; char *temp;
@ -254,6 +299,7 @@ static void decompressBuild(ArchiveT *archive) {
DEL(temp); DEL(temp);
//***TODO*** Process build results. //***TODO*** Process build results.
message(MSG_INFO, "Processing build results");
} }
@ -922,39 +968,15 @@ EVENT void menuProjectBuildTargets(GtkWidget *object, gpointer userData) {
EVENT void menuProjectBuildCookRecipes(GtkWidget *object, gpointer userData) { EVENT void menuProjectBuildCookRecipes(GtkWidget *object, gpointer userData) {
ProjectDataT *self = (ProjectDataT *)userData; ProjectDataT *self = (ProjectDataT *)userData;
int i; CompilerContextT *ctx;
char *raw;
char *recipe;
int result;
// Only one cook at a time. Should not be able to happen. // Are there recipes to cook?
if (_cookingProjectData) { if (arrlen(self->recipes) > 0) {
message(MSG_ERROR, "Cook currently running"); self->tempInteger = -1; // Tell cookFinished() we're just starting.
return; ctx = compilerNewContext(cookFinished, self);
cookFinished(&ctx);
} }
// Remember who started the cook.
_cookingProjectData = self;
for (i=0; i<arrlen(self->recipes); i++) {
// Build pathnames.
cwk_path_change_basename(self->windowData.filename, self->recipes[i]->key, __utilFilenameBuffer, sizeof(__utilFilenameBuffer));
raw = strdup(__utilFilenameBuffer);
cwk_path_change_basename(self->windowData.filename, self->recipes[i]->value, __utilFilenameBuffer, sizeof(__utilFilenameBuffer));
recipe = strdup(__utilFilenameBuffer);
// Run it!
message(MSG_INFO, "Cooking %s", self->recipes[i]->key);
result = compilerRunRecipe(recipe, raw, self->windowData.path, self);
if (result != 0) {
//***TODO*** Not all negative returns are severe.
message(result > 0 ? MSG_ERROR : MSG_SEVERE, "Recipe %s returned %d", self->recipes[i]->value, result);
}
message(MSG_INFO, "Finished Cooking");
}
_cookingProjectData = NULL;
} }
@ -975,8 +997,6 @@ EVENT void menuProjectBuildBuild(GtkWidget *object, gpointer userData) {
gboolean archPrinted; gboolean archPrinted;
FILE *out; FILE *out;
menuProjectBuildCookRecipes(object, userData);
ssh = NEW(SSHT); ssh = NEW(SSHT);
ssh = sshConnect(self->buildHost, self->buildSSHPort, self->buildUser, self->buildPassword); ssh = sshConnect(self->buildHost, self->buildSSHPort, self->buildUser, self->buildPassword);

View file

@ -118,6 +118,8 @@ SSHT *sshConnect(char *hostname, uint16_t port, char *user, char *password) {
} }
} }
//***TODO*** Move SFTP session setup here so we don't keep repeating it.
return data; return data;
} }
@ -125,6 +127,8 @@ SSHT *sshConnect(char *hostname, uint16_t port, char *user, char *password) {
void sshDisconnect(SSHT **sshData) { void sshDisconnect(SSHT **sshData) {
SSHT *data = *sshData; SSHT *data = *sshData;
//***TODO*** Move SFTP session teardown here so we don't keep repeating it.
libssh2_session_disconnect(data->session, "Normal Shutdown."); libssh2_session_disconnect(data->session, "Normal Shutdown.");
libssh2_session_free(data->session); libssh2_session_free(data->session);
socketclose(data->sock); socketclose(data->sock);

View file

@ -233,6 +233,7 @@ gboolean utilDecompressUpdate(gpointer userData) {
if (toClose >= 0) { if (toClose >= 0) {
a = _activeArchives[toClose]; a = _activeArchives[toClose];
arrdel(_activeArchives, toClose);
if (a->callback) { if (a->callback) {
a->callback(a); a->callback(a);
} }